package com.clark.func;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.Closeable;
import java.io.Console;
import java.io.EOFException;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.Checksum;

import com.clark.io.AgeFileFilter;
import com.clark.io.AndFileFilter;
import com.clark.io.DelegateFileFilter;
import com.clark.io.DirectoryFileFilter;
import com.clark.io.FalseFileFilter;
import com.clark.io.FileExistsException;
import com.clark.io.FileFileFilter;
import com.clark.io.IOCase;
import com.clark.io.IOExceptionWithCause;
import com.clark.io.IOFileFilter;
import com.clark.io.LineIterator;
import com.clark.io.MagicNumberFileFilter;
import com.clark.io.NameFileFilter;
import com.clark.io.NotFileFilter;
import com.clark.io.NullOutputStream;
import com.clark.io.OrFileFilter;
import com.clark.io.PrefixFileFilter;
import com.clark.io.SizeFileFilter;
import com.clark.io.StringBuilderWriter;
import com.clark.io.SuffixFileFilter;
import com.clark.io.ThreadMonitor;
import com.clark.io.TrueFileFilter;
import com.clark.lang.CloneFailedException;
import com.clark.lang.EqualsBuilder;
import com.clark.lang.FastDateFormat;
import com.clark.lang.SerializationException;
import com.clark.lang.ToStringBuilder;
import com.clark.lang.ToStringStyle;

public final class Functions {

    // ///////////////////////////////////////////
    //
    // Thread
    //
    // ///////////////////////////////////////////

    public static Thread currentThread() {
        return Thread.currentThread();
    }

    public static boolean isCurrentHoldsLock(Object lockObj) {
        return Thread.holdsLock(lockObj);
    }

    public static void currentYield() {
        Thread.yield();
    }

    public static void setCurrentUncaughtExceptionHandler(
            Thread.UncaughtExceptionHandler handler) {
        Thread.currentThread().setUncaughtExceptionHandler(handler);
    }

    public static Thread.UncaughtExceptionHandler getCurrentUncaughtExceptionHandler() {
        return Thread.currentThread().getUncaughtExceptionHandler();
    }

    public static void currentSleep(long millis) throws InterruptedException {
        Thread.sleep(millis);
    }

    public static Thread.State getCurrentState() {
        return Thread.currentThread().getState();
    }

    // ///////////////////////////////////////////
    //
    // Runtime
    //
    // ///////////////////////////////////////////

    public static int availableProcessors() {
        return Runtime.getRuntime().availableProcessors();
    }

    public static void loadLibraryPath(String filePath) {
        Runtime.getRuntime().load(filePath);
    }

    public static void loadLibrary(String libName) {
        Runtime.getRuntime().loadLibrary(libName);
    }

    public static long freeMemory() {
        return Runtime.getRuntime().freeMemory();
    }

    public static long maxMemory() {
        return Runtime.getRuntime().maxMemory();
    }

    public static long totalMemory() {
        return Runtime.getRuntime().totalMemory();
    }

    public static void addShutdownHook(Thread hook) {
        Runtime.getRuntime().addShutdownHook(hook);
    }

    public static void removeShutdownHook(Thread hook) {
        Runtime.getRuntime().removeShutdownHook(hook);
    }

    public static Process execExternalCmd(String[] cmd, String[] envp, File dir)
            throws IOException {
        return Runtime.getRuntime().exec(cmd, envp, dir);
    }

    public static Process execExternalCmd(String... cmd) throws IOException {
        return Runtime.getRuntime().exec(cmd);
    }

    // ///////////////////////////////////////////
    //
    // System
    //
    // ///////////////////////////////////////////

    public static void println() {
        System.out.println();
    }

    public static void println(boolean x) {
        System.out.println(x);
    }

    public static void println(char x) {
        System.out.println(x);
    }

    public static void println(int x) {
        System.out.println(x);
    }

    public static void println(long x) {
        System.out.println(x);
    }

    public static void println(float x) {
        System.out.println(x);
    }

    public static void println(double x) {
        System.out.println(x);
    }

    public static void println(char x[]) {
        System.out.println(x);
    }

    public static void println(String x) {
        System.out.println(x);
    }

    public static void println(Object x) {
        System.out.println(x);
    }

    public static void printf(Locale locale, String format, Object... args) {
        System.out.printf(locale, format, args);
    }

    public static void printf(String format, Object... args) {
        System.out.printf(format, args);
    }

    // ////////////////////////////////////////////////////
    public static void errPrint(boolean b) {
        System.err.print(b);
    }

    public static void errPrint(char c) {
        System.err.print(c);
    }

    public static void errPrint(int i) {
        System.err.print(i);
    }

    public static void errPrint(long l) {
        System.err.print(l);
    }

    public static void errPrint(float f) {
        System.err.print(f);
    }

    public static void errPrint(double d) {
        System.err.print(d);
    }

    public static void errPrint(char s[]) {
        System.err.print(s);
    }

    public static void errPrint(String s) {
        System.err.print(s);
    }

    public static void errPrint(Object obj) {
        System.err.print(obj);
    }

    public static void errPrintln() {
        System.err.println();
    }

    public static void errPrintln(boolean x) {
        System.err.println(x);
    }

    public static void errPrintln(char x) {
        System.err.println(x);
    }

    public static void errPrintln(int x) {
        System.err.println(x);
    }

    public static void errPrintln(long x) {
        System.err.println(x);
    }

    public static void errPrintln(float x) {
        System.err.println(x);
    }

    public static void errPrintln(double x) {
        System.err.println(x);
    }

    public static void errPrintln(char x[]) {
        System.err.println(x);
    }

    public static void errPrintln(String x) {
        System.err.println(x);
    }

    public static void errPrintln(Object x) {
        System.err.println(x);
    }

    public static void errPrintf(Locale locale, String format, Object... args) {
        System.err.printf(locale, format, args);
    }

    public static void errPrintf(String format, Object... args) {
        System.err.printf(format, args);
    }

    // /////////////////////////////////////////////////////

    public static Console console() {
        return System.console();
    }

    public static long nanoTime() {
        return System.nanoTime();
    }

    public static void arraycopy(Object src, int srcPos, Object dest,
            int destPos, int length) {
        System.arraycopy(src, srcPos, dest, destPos, length);
    }

    public static int identityHashCode(Object obj) {
        return System.identityHashCode(obj);
    }

    public static void exit(int status) {
        System.exit(status);
    }

    // ///////////////////////////////////////////
    //
    // StringUtils
    //
    // ///////////////////////////////////////////

    public static String stringFormat(String format, Object... args) {
        return String.format(format, args);
    }

    /**
     * The empty String <code>""</code>.
     * 
     * @since 2.0
     */
    public static final String EMPTY_STRING = "";

    /**
     * Represents a failed index search.
     * 
     * @since 2.1
     */
    public static final int INDEX_NOT_FOUND = -1;

    /**
     * <p>
     * The maximum size to which the padding constant(s) can expand.
     * </p>
     */
    private static final int PAD_LIMIT = 8192;

    /**
     * <p>
     * Checks if a CharSequence is empty ("") or null.
     * </p>
     * 
     * <pre>
     * isEmpty(null)      = true
     * isEmpty("")        = true
     * isEmpty(" ")       = false
     * isEmpty("bob")     = false
     * isEmpty("  bob  ") = false
     * </pre>
     * 
     * <p>
     * NOTE: This method changed in Lang version 2.0. It no longer trims the
     * CharSequence. That functionality is available in isBlank().
     * </p>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if the CharSequence is empty or null
     * @since 3.0 Changed signature from isEmpty(String) to
     *        isEmpty(CharSequence)
     */
    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    /**
     * <p>
     * Checks if a CharSequence is not empty ("") and not null.
     * </p>
     * 
     * <pre>
     * isNotEmpty(null)      = false
     * isNotEmpty("")        = false
     * isNotEmpty(" ")       = true
     * isNotEmpty("bob")     = true
     * isNotEmpty("  bob  ") = true
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if the CharSequence is not empty and not null
     * @since 3.0 Changed signature from isNotEmpty(String) to
     *        isNotEmpty(CharSequence)
     */
    public static boolean isNotEmpty(CharSequence cs) {
        return !isEmpty(cs);
    }

    /**
     * <p>
     * Checks if a CharSequence is whitespace, empty ("") or null.
     * </p>
     * 
     * <pre>
     * isBlank(null)      = true
     * isBlank("")        = true
     * isBlank(" ")       = true
     * isBlank("bob")     = false
     * isBlank("  bob  ") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if the CharSequence is null, empty or
     *         whitespace
     * @since 2.0
     * @since 3.0 Changed signature from isBlank(String) to
     *        isBlank(CharSequence)
     */
    public static boolean isBlank(CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(cs.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if a CharSequence is not empty (""), not null and not whitespace
     * only.
     * </p>
     * 
     * <pre>
     * isNotBlank(null)      = false
     * isNotBlank("")        = false
     * isNotBlank(" ")       = false
     * isNotBlank("bob")     = true
     * isNotBlank("  bob  ") = true
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if the CharSequence is not empty and not null
     *         and not whitespace
     * @since 2.0
     * @since 3.0 Changed signature from isNotBlank(String) to
     *        isNotBlank(CharSequence)
     */
    public static boolean isNotBlank(CharSequence cs) {
        return !isBlank(cs);
    }

    // Trim
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Removes control characters (char &lt;= 32) from both ends of this String,
     * handling <code>null</code> by returning <code>null</code>.
     * </p>
     * 
     * <p>
     * The String is trimmed using {@link String#trim()}. Trim removes start and
     * end characters &lt;= 32. To strip whitespace use {@link #strip(String)}.
     * </p>
     * 
     * <p>
     * To trim your choice of characters, use the {@link #strip(String, String)}
     * methods.
     * </p>
     * 
     * <pre>
     * trim(null)          = null
     * trim("")            = ""
     * trim("     ")       = ""
     * trim("abc")         = "abc"
     * trim("    abc    ") = "abc"
     * </pre>
     * 
     * @param str
     *            the String to be trimmed, may be null
     * @return the trimmed string, <code>null</code> if null String input
     */
    public static String trim(String str) {
        return str == null ? null : str.trim();
    }

    /**
     * <p>
     * Removes control characters (char &lt;= 32) from both ends of this String
     * returning <code>null</code> if the String is empty ("") after the trim or
     * if it is <code>null</code>.
     * 
     * <p>
     * The String is trimmed using {@link String#trim()}. Trim removes start and
     * end characters &lt;= 32. To strip whitespace use
     * {@link #stripToNull(String)}.
     * </p>
     * 
     * <pre>
     * trimToNull(null)          = null
     * trimToNull("")            = null
     * trimToNull("     ")       = null
     * trimToNull("abc")         = "abc"
     * trimToNull("    abc    ") = "abc"
     * </pre>
     * 
     * @param str
     *            the String to be trimmed, may be null
     * @return the trimmed String, <code>null</code> if only chars &lt;= 32,
     *         empty or null String input
     * @since 2.0
     */
    public static String trimToNull(String str) {
        String ts = trim(str);
        return isEmpty(ts) ? null : ts;
    }

    /**
     * <p>
     * Removes control characters (char &lt;= 32) from both ends of this String
     * returning an empty String ("") if the String is empty ("") after the trim
     * or if it is <code>null</code>.
     * 
     * <p>
     * The String is trimmed using {@link String#trim()}. Trim removes start and
     * end characters &lt;= 32. To strip whitespace use
     * {@link #stripToEmpty(String)}.
     * </p>
     * 
     * <pre>
     * trimToEmpty(null)          = ""
     * trimToEmpty("")            = ""
     * trimToEmpty("     ")       = ""
     * trimToEmpty("abc")         = "abc"
     * trimToEmpty("    abc    ") = "abc"
     * </pre>
     * 
     * @param str
     *            the String to be trimmed, may be null
     * @return the trimmed String, or an empty String if <code>null</code> input
     * @since 2.0
     */
    public static String trimToEmpty(String str) {
        return str == null ? EMPTY_STRING : str.trim();
    }

    // Stripping
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Strips whitespace from the start and end of a String.
     * </p>
     * 
     * <p>
     * This is similar to {@link #trim(String)} but removes whitespace.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * strip(null)     = null
     * strip("")       = ""
     * strip("   ")    = ""
     * strip("abc")    = "abc"
     * strip("  abc")  = "abc"
     * strip("abc  ")  = "abc"
     * strip(" abc ")  = "abc"
     * strip(" ab c ") = "ab c"
     * </pre>
     * 
     * @param str
     *            the String to remove whitespace from, may be null
     * @return the stripped String, <code>null</code> if null String input
     */
    public static String strip(String str) {
        return strip(str, null);
    }

    /**
     * <p>
     * Strips whitespace from the start and end of a String returning
     * <code>null</code> if the String is empty ("") after the strip.
     * </p>
     * 
     * <p>
     * This is similar to {@link #trimToNull(String)} but removes whitespace.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * stripToNull(null)     = null
     * stripToNull("")       = null
     * stripToNull("   ")    = null
     * stripToNull("abc")    = "abc"
     * stripToNull("  abc")  = "abc"
     * stripToNull("abc  ")  = "abc"
     * stripToNull(" abc ")  = "abc"
     * stripToNull(" ab c ") = "ab c"
     * </pre>
     * 
     * @param str
     *            the String to be stripped, may be null
     * @return the stripped String, <code>null</code> if whitespace, empty or
     *         null String input
     * @since 2.0
     */
    public static String stripToNull(String str) {
        if (str == null) {
            return null;
        }
        str = strip(str, null);
        return str.length() == 0 ? null : str;
    }

    /**
     * <p>
     * Strips whitespace from the start and end of a String returning an empty
     * String if <code>null</code> input.
     * </p>
     * 
     * <p>
     * This is similar to {@link #trimToEmpty(String)} but removes whitespace.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * stripToEmpty(null)     = ""
     * stripToEmpty("")       = ""
     * stripToEmpty("   ")    = ""
     * stripToEmpty("abc")    = "abc"
     * stripToEmpty("  abc")  = "abc"
     * stripToEmpty("abc  ")  = "abc"
     * stripToEmpty(" abc ")  = "abc"
     * stripToEmpty(" ab c ") = "ab c"
     * </pre>
     * 
     * @param str
     *            the String to be stripped, may be null
     * @return the trimmed String, or an empty String if <code>null</code> input
     * @since 2.0
     */
    public static String stripToEmpty(String str) {
        return str == null ? EMPTY_STRING : strip(str, null);
    }

    /**
     * <p>
     * Strips any of a set of characters from the start and end of a String.
     * This is similar to {@link String#trim()} but allows the characters to be
     * stripped to be controlled.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. An empty
     * string ("") input returns the empty string.
     * </p>
     * 
     * <p>
     * If the stripChars String is <code>null</code>, whitespace is stripped as
     * defined by {@link Character#isWhitespace(char)}. Alternatively use
     * {@link #strip(String)}.
     * </p>
     * 
     * <pre>
     * strip(null, *)          = null
     * strip("", *)            = ""
     * strip("abc", null)      = "abc"
     * strip("  abc", null)    = "abc"
     * strip("abc  ", null)    = "abc"
     * strip(" abc ", null)    = "abc"
     * strip("  abcyx", "xyz") = "  abc"
     * </pre>
     * 
     * @param str
     *            the String to remove characters from, may be null
     * @param stripChars
     *            the characters to remove, null treated as whitespace
     * @return the stripped String, <code>null</code> if null String input
     */
    public static String strip(String str, String stripChars) {
        if (isEmpty(str)) {
            return str;
        }
        str = stripStart(str, stripChars);
        return stripEnd(str, stripChars);
    }

    /**
     * <p>
     * Strips any of a set of characters from the start of a String.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. An empty
     * string ("") input returns the empty string.
     * </p>
     * 
     * <p>
     * If the stripChars String is <code>null</code>, whitespace is stripped as
     * defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * stripStart(null, *)          = null
     * stripStart("", *)            = ""
     * stripStart("abc", "")        = "abc"
     * stripStart("abc", null)      = "abc"
     * stripStart("  abc", null)    = "abc"
     * stripStart("abc  ", null)    = "abc  "
     * stripStart(" abc ", null)    = "abc "
     * stripStart("yxabc  ", "xyz") = "abc  "
     * </pre>
     * 
     * @param str
     *            the String to remove characters from, may be null
     * @param stripChars
     *            the characters to remove, null treated as whitespace
     * @return the stripped String, <code>null</code> if null String input
     */
    public static String stripStart(String str, String stripChars) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return str;
        }
        int start = 0;
        if (stripChars == null) {
            while ((start != strLen)
                    && Character.isWhitespace(str.charAt(start))) {
                start++;
            }
        } else if (stripChars.length() == 0) {
            return str;
        } else {
            while ((start != strLen)
                    && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) {
                start++;
            }
        }
        return str.substring(start);
    }

    /**
     * <p>
     * Strips any of a set of characters from the end of a String.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. An empty
     * string ("") input returns the empty string.
     * </p>
     * 
     * <p>
     * If the stripChars String is <code>null</code>, whitespace is stripped as
     * defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * stripEnd(null, *)          = null
     * stripEnd("", *)            = ""
     * stripEnd("abc", "")        = "abc"
     * stripEnd("abc", null)      = "abc"
     * stripEnd("  abc", null)    = "  abc"
     * stripEnd("abc  ", null)    = "abc"
     * stripEnd(" abc ", null)    = " abc"
     * stripEnd("  abcyx", "xyz") = "  abc"
     * </pre>
     * 
     * @param str
     *            the String to remove characters from, may be null
     * @param stripChars
     *            the characters to remove, null treated as whitespace
     * @return the stripped String, <code>null</code> if null String input
     */
    public static String stripEnd(String str, String stripChars) {
        int end;
        if (str == null || (end = str.length()) == 0) {
            return str;
        }

        if (stripChars == null) {
            while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
                end--;
            }
        } else if (stripChars.length() == 0) {
            return str;
        } else {
            while ((end != 0)
                    && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) {
                end--;
            }
        }
        return str.substring(0, end);
    }

    // StripAll
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Strips whitespace from the start and end of every String in an array.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <p>
     * A new array is returned each time, except for length zero. A
     * <code>null</code> array will return <code>null</code>. An empty array
     * will return itself. A <code>null</code> array entry will be ignored.
     * </p>
     * 
     * <pre>
     * stripAll(null)             = null
     * stripAll([])               = []
     * stripAll(["abc", "  abc"]) = ["abc", "abc"]
     * stripAll(["abc  ", null])  = ["abc", null]
     * </pre>
     * 
     * @param strs
     *            the array to remove whitespace from, may be null
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAll(String[] strs) {
        return stripAll(strs, null);
    }

    /**
     * <p>
     * Strips any of a set of characters from the start and end of every String
     * in an array.
     * </p>
     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
     * 
     * <p>
     * A new array is returned each time, except for length zero. A
     * <code>null</code> array will return <code>null</code>. An empty array
     * will return itself. A <code>null</code> array entry will be ignored. A
     * <code>null</code> stripChars will strip whitespace as defined by
     * {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * stripAll(null, *)                = null
     * stripAll([], *)                  = []
     * stripAll(["abc", "  abc"], null) = ["abc", "abc"]
     * stripAll(["abc  ", null], null)  = ["abc", null]
     * stripAll(["abc  ", null], "yz")  = ["abc  ", null]
     * stripAll(["yabcz", null], "yz")  = ["abc", null]
     * </pre>
     * 
     * @param strs
     *            the array to remove characters from, may be null
     * @param stripChars
     *            the characters to remove, null treated as whitespace
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAll(String[] strs, String stripChars) {
        int strsLen;
        if (strs == null || (strsLen = strs.length) == 0) {
            return strs;
        }
        String[] newArr = new String[strsLen];
        for (int i = 0; i < strsLen; i++) {
            newArr[i] = strip(strs[i], stripChars);
        }
        return newArr;
    }

    /**
     * <p>
     * Removes the accents from a string.
     * </p>
     * <p>
     * NOTE: This is a JDK 1.6 method, it will fail on JDK 1.5.
     * </p>
     * 
     * <pre>
     * stripAccents(null)                = null
     * stripAccents("")                  = ""
     * stripAccents("control")           = "control"
     * stripAccents("&ecute;clair")      = "eclair"
     * </pre>
     * 
     * @param input
     *            String to be stripped
     * @return String without accents on the text
     * 
     * @since 3.0
     */
    public static String stripAccents(String input) {
        if (input == null) {
            return null;
        }
        if (isJavaVersionAtLeast(1.6f)) {

            // String decomposed = Normalizer.normalize(input,
            // Normalizer.Form.NFD);

            // START of 1.5 reflection - in 1.6 use the line commented out above
            try {
                // get java.text.Normalizer.Form class
                Class<?> normalizerFormClass = getClass(
                        "java.text.Normalizer$Form", false);

                // get Normlizer class
                Class<?> normalizerClass = getClass("java.text.Normalizer",
                        false);

                // get static method on Normalizer
                java.lang.reflect.Method method = normalizerClass.getMethod(
                        "normalize", CharSequence.class, normalizerFormClass);

                // get Normalizer.NFD field
                java.lang.reflect.Field nfd = normalizerFormClass
                        .getField("NFD");

                // invoke method
                String decomposed = (String) method.invoke(null, input,
                        nfd.get(null));
                // END of 1.5 reflection

                java.util.regex.Pattern accentPattern = java.util.regex.Pattern
                        .compile("\\p{InCombiningDiacriticalMarks}+");
                return accentPattern.matcher(decomposed).replaceAll("");
            } catch (ClassNotFoundException cnfe) {
                throw new RuntimeException(
                        "ClassNotFoundException occurred during 1.6 backcompat code",
                        cnfe);
            } catch (NoSuchMethodException nsme) {
                throw new RuntimeException(
                        "NoSuchMethodException occurred during 1.6 backcompat code",
                        nsme);
            } catch (NoSuchFieldException nsfe) {
                throw new RuntimeException(
                        "NoSuchFieldException occurred during 1.6 backcompat code",
                        nsfe);
            } catch (IllegalAccessException iae) {
                throw new RuntimeException(
                        "IllegalAccessException occurred during 1.6 backcompat code",
                        iae);
            } catch (IllegalArgumentException iae) {
                throw new RuntimeException(
                        "IllegalArgumentException occurred during 1.6 backcompat code",
                        iae);
            } catch (java.lang.reflect.InvocationTargetException ite) {
                throw new RuntimeException(
                        "InvocationTargetException occurred during 1.6 backcompat code",
                        ite);
            } catch (SecurityException se) {
                throw new RuntimeException(
                        "SecurityException occurred during 1.6 backcompat code",
                        se);
            }
        } else {
            throw new UnsupportedOperationException(
                    "The stripAccents(String) method is not supported until Java 1.6");
        }
    }

    // Equals
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Compares two CharSequences, returning <code>true</code> if they are
     * equal.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered to be equal. The comparison is case sensitive.
     * </p>
     * 
     * <pre>
     * equals(null, null)   = true
     * equals(null, "abc")  = false
     * equals("abc", null)  = false
     * equals("abc", "abc") = true
     * equals("abc", "ABC") = false
     * </pre>
     * 
     * @see java.lang.String#equals(Object)
     * @param cs1
     *            the first CharSequence, may be null
     * @param cs2
     *            the second CharSequence, may be null
     * @return <code>true</code> if the CharSequences are equal, case sensitive,
     *         or both <code>null</code>
     * @since 3.0 Changed signature from equals(String, String) to
     *        equals(CharSequence, CharSequence)
     */
    public static boolean equals(CharSequence cs1, CharSequence cs2) {
        return cs1 == null ? cs2 == null : cs1.equals(cs2);
    }

    /**
     * <p>
     * Compares two Strings, returning <code>true</code> if they are equal
     * ignoring the case.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered equal. Comparison is case insensitive.
     * </p>
     * 
     * <pre>
     * equalsIgnoreCase(null, null)   = true
     * equalsIgnoreCase(null, "abc")  = false
     * equalsIgnoreCase("abc", null)  = false
     * equalsIgnoreCase("abc", "abc") = true
     * equalsIgnoreCase("abc", "ABC") = true
     * </pre>
     * 
     * @see java.lang.String#equalsIgnoreCase(String)
     * @param str1
     *            the first String, may be null
     * @param str2
     *            the second String, may be null
     * @return <code>true</code> if the Strings are equal, case insensitive, or
     *         both <code>null</code>
     */
    public static boolean equalsIgnoreCase(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
    }

    // IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the first index within a String, handling <code>null</code>. This
     * method uses {@link String#indexOf(int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String will return
     * <code>INDEX_NOT_FOUND (-1)</code>.
     * </p>
     * 
     * <pre>
     * indexOf(null, *)         = -1
     * indexOf("", *)           = -1
     * indexOf("aabaabaa", 'a') = 0
     * indexOf("aabaabaa", 'b') = 2
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChar
     *            the character to find
     * @return the first index of the search character, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int indexOf(String str, int searchChar) {
        if (isEmpty(str)) {
            return INDEX_NOT_FOUND;
        }
        return str.indexOf(searchChar);
    }

    /**
     * <p>
     * Finds the first index within a String from a start position, handling
     * <code>null</code>. This method uses {@link String#indexOf(int, int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String will return
     * <code>(INDEX_NOT_FOUND) -1</code>. A negative start position is treated
     * as zero. A start position greater than the string length returns
     * <code>-1</code>.
     * </p>
     * 
     * <pre>
     * indexOf(null, *, *)          = -1
     * indexOf("", *, *)            = -1
     * indexOf("aabaabaa", 'b', 0)  = 2
     * indexOf("aabaabaa", 'b', 3)  = 5
     * indexOf("aabaabaa", 'b', 9)  = -1
     * indexOf("aabaabaa", 'b', -1) = 2
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChar
     *            the character to find
     * @param startPos
     *            the start position, negative treated as zero
     * @return the first index of the search character, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int indexOf(String str, int searchChar, int startPos) {
        if (isEmpty(str)) {
            return INDEX_NOT_FOUND;
        }
        return str.indexOf(searchChar, startPos);
    }

    /**
     * <p>
     * Finds the first index within a String, handling <code>null</code>. This
     * method uses {@link String#indexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * indexOf(null, *)          = -1
     * indexOf(*, null)          = -1
     * indexOf("", "")           = 0
     * indexOf("", *)            = -1 (except when * = "")
     * indexOf("aabaabaa", "a")  = 0
     * indexOf("aabaabaa", "b")  = 2
     * indexOf("aabaabaa", "ab") = 1
     * indexOf("aabaabaa", "")   = 0
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int indexOf(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        return str.indexOf(searchStr);
    }

    /**
     * <p>
     * Finds the first index within a String, handling <code>null</code>. This
     * method uses {@link String#indexOf(String, int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position is treated as zero. An empty ("") search String always matches.
     * A start position greater than the string length only matches an empty
     * search String.
     * </p>
     * 
     * <pre>
     * indexOf(null, *, *)          = -1
     * indexOf(*, null, *)          = -1
     * indexOf("", "", 0)           = 0
     * indexOf("", *, 0)            = -1 (except when * = "")
     * indexOf("aabaabaa", "a", 0)  = 0
     * indexOf("aabaabaa", "b", 0)  = 2
     * indexOf("aabaabaa", "ab", 0) = 1
     * indexOf("aabaabaa", "b", 3)  = 5
     * indexOf("aabaabaa", "b", 9)  = -1
     * indexOf("aabaabaa", "b", -1) = 2
     * indexOf("aabaabaa", "", 2)   = 2
     * indexOf("abc", "", 9)        = 3
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param startPos
     *            the start position, negative treated as zero
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int indexOf(String str, String searchStr, int startPos) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        return str.indexOf(searchStr, startPos);
    }

    /**
     * <p>
     * Finds the n-th index within a String, handling <code>null</code>. This
     * method uses {@link String#indexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * ordinalIndexOf(null, *, *)          = -1
     * ordinalIndexOf(*, null, *)          = -1
     * ordinalIndexOf("", "", *)           = 0
     * ordinalIndexOf("aabaabaa", "a", 1)  = 0
     * ordinalIndexOf("aabaabaa", "a", 2)  = 1
     * ordinalIndexOf("aabaabaa", "b", 1)  = 2
     * ordinalIndexOf("aabaabaa", "b", 2)  = 5
     * ordinalIndexOf("aabaabaa", "ab", 1) = 1
     * ordinalIndexOf("aabaabaa", "ab", 2) = 4
     * ordinalIndexOf("aabaabaa", "", 1)   = 0
     * ordinalIndexOf("aabaabaa", "", 2)   = 0
     * </pre>
     * 
     * <p>
     * Note that 'head(String str, int n)' may be implemented as:
     * </p>
     * 
     * <pre>
     * str.substring(0, lastOrdinalIndexOf(str, &quot;\n&quot;, n))
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param ordinal
     *            the n-th <code>searchStr</code> to find
     * @return the n-th index of the search String, <code>-1</code> (
     *         <code>INDEX_NOT_FOUND</code>) if no match or <code>null</code>
     *         string input
     * @since 2.1
     */
    public static int ordinalIndexOf(String str, String searchStr, int ordinal) {
        return ordinalIndexOf(str, searchStr, ordinal, false);
    }

    /**
     * <p>
     * Finds the n-th index within a String, handling <code>null</code>. This
     * method uses {@link String#indexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>.
     * </p>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param ordinal
     *            the n-th <code>searchStr</code> to find
     * @param lastIndex
     *            true if lastOrdinalIndexOf() otherwise false if
     *            ordinalIndexOf()
     * @return the n-th index of the search String, <code>-1</code> (
     *         <code>INDEX_NOT_FOUND</code>) if no match or <code>null</code>
     *         string input
     */
    // Shared code between ordinalIndexOf(String,String,int) and
    // lastOrdinalIndexOf(String,String,int)
    private static int ordinalIndexOf(String str, String searchStr,
            int ordinal, boolean lastIndex) {
        if (str == null || searchStr == null || ordinal <= 0) {
            return INDEX_NOT_FOUND;
        }
        if (searchStr.length() == 0) {
            return lastIndex ? str.length() : 0;
        }
        int found = 0;
        int index = lastIndex ? str.length() : INDEX_NOT_FOUND;
        do {
            if (lastIndex) {
                index = str.lastIndexOf(searchStr, index - 1);
            } else {
                index = str.indexOf(searchStr, index + 1);
            }
            if (index < 0) {
                return index;
            }
            found++;
        } while (found < ordinal);
        return index;
    }

    /**
     * <p>
     * Case in-sensitive find of the first index within a String.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position is treated as zero. An empty ("") search String always matches.
     * A start position greater than the string length only matches an empty
     * search String.
     * </p>
     * 
     * <pre>
     * indexOfIgnoreCase(null, *)          = -1
     * indexOfIgnoreCase(*, null)          = -1
     * indexOfIgnoreCase("", "")           = 0
     * indexOfIgnoreCase("aabaabaa", "a")  = 0
     * indexOfIgnoreCase("aabaabaa", "b")  = 2
     * indexOfIgnoreCase("aabaabaa", "ab") = 1
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.5
     */
    public static int indexOfIgnoreCase(String str, String searchStr) {
        return indexOfIgnoreCase(str, searchStr, 0);
    }

    /**
     * <p>
     * Case in-sensitive find of the first index within a String from the
     * specified position.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position is treated as zero. An empty ("") search String always matches.
     * A start position greater than the string length only matches an empty
     * search String.
     * </p>
     * 
     * <pre>
     * indexOfIgnoreCase(null, *, *)          = -1
     * indexOfIgnoreCase(*, null, *)          = -1
     * indexOfIgnoreCase("", "", 0)           = 0
     * indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
     * indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
     * indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
     * indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
     * indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
     * indexOfIgnoreCase("aabaabaa", "B", -1) = 2
     * indexOfIgnoreCase("aabaabaa", "", 2)   = 2
     * indexOfIgnoreCase("abc", "", 9)        = 3
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param startPos
     *            the start position, negative treated as zero
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.5
     */
    public static int indexOfIgnoreCase(String str, String searchStr,
            int startPos) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        if (startPos < 0) {
            startPos = 0;
        }
        int endLimit = (str.length() - searchStr.length()) + 1;
        if (startPos > endLimit) {
            return INDEX_NOT_FOUND;
        }
        if (searchStr.length() == 0) {
            return startPos;
        }
        for (int i = startPos; i < endLimit; i++) {
            if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    // LastIndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the last index within a String, handling <code>null</code>. This
     * method uses {@link String#lastIndexOf(int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * lastIndexOf(null, *)         = -1
     * lastIndexOf("", *)           = -1
     * lastIndexOf("aabaabaa", 'a') = 7
     * lastIndexOf("aabaabaa", 'b') = 5
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChar
     *            the character to find
     * @return the last index of the search character, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int lastIndexOf(String str, int searchChar) {
        if (isEmpty(str)) {
            return INDEX_NOT_FOUND;
        }
        return str.lastIndexOf(searchChar);
    }

    /**
     * <p>
     * Finds the last index within a String from a start position, handling
     * <code>null</code>. This method uses {@link String#lastIndexOf(int, int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String will return <code>-1</code>. A
     * negative start position returns <code>-1</code>. A start position greater
     * than the string length searches the whole string.
     * </p>
     * 
     * <pre>
     * lastIndexOf(null, *, *)          = -1
     * lastIndexOf("", *,  *)           = -1
     * lastIndexOf("aabaabaa", 'b', 8)  = 5
     * lastIndexOf("aabaabaa", 'b', 4)  = 2
     * lastIndexOf("aabaabaa", 'b', 0)  = -1
     * lastIndexOf("aabaabaa", 'b', 9)  = 5
     * lastIndexOf("aabaabaa", 'b', -1) = -1
     * lastIndexOf("aabaabaa", 'a', 0)  = 0
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChar
     *            the character to find
     * @param startPos
     *            the start position
     * @return the last index of the search character, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int lastIndexOf(String str, int searchChar, int startPos) {
        if (isEmpty(str)) {
            return INDEX_NOT_FOUND;
        }
        return str.lastIndexOf(searchChar, startPos);
    }

    /**
     * <p>
     * Finds the last index within a String, handling <code>null</code>. This
     * method uses {@link String#lastIndexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * lastIndexOf(null, *)          = -1
     * lastIndexOf(*, null)          = -1
     * lastIndexOf("", "")           = 0
     * lastIndexOf("aabaabaa", "a")  = 7
     * lastIndexOf("aabaabaa", "b")  = 5
     * lastIndexOf("aabaabaa", "ab") = 4
     * lastIndexOf("aabaabaa", "")   = 8
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return the last index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int lastIndexOf(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        return str.lastIndexOf(searchStr);
    }

    /**
     * <p>
     * Finds the n-th last index within a String, handling <code>null</code>.
     * This method uses {@link String#lastIndexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * lastOrdinalIndexOf(null, *, *)          = -1
     * lastOrdinalIndexOf(*, null, *)          = -1
     * lastOrdinalIndexOf("", "", *)           = 0
     * lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
     * lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
     * lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
     * lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
     * lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
     * lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
     * lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
     * lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
     * </pre>
     * 
     * <p>
     * Note that 'tail(String str, int n)' may be implemented as:
     * </p>
     * 
     * <pre>
     * str.substring(lastOrdinalIndexOf(str, &quot;\n&quot;, n) + 1)
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param ordinal
     *            the n-th last <code>searchStr</code> to find
     * @return the n-th last index of the search String, <code>-1</code> (
     *         <code>INDEX_NOT_FOUND</code>) if no match or <code>null</code>
     *         string input
     * @since 2.5
     */
    public static int lastOrdinalIndexOf(String str, String searchStr,
            int ordinal) {
        return ordinalIndexOf(str, searchStr, ordinal, true);
    }

    /**
     * <p>
     * Finds the first index within a String, handling <code>null</code>. This
     * method uses {@link String#lastIndexOf(String, int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position returns <code>-1</code>. An empty ("") search String always
     * matches unless the start position is negative. A start position greater
     * than the string length searches the whole string.
     * </p>
     * 
     * <pre>
     * lastIndexOf(null, *, *)          = -1
     * lastIndexOf(*, null, *)          = -1
     * lastIndexOf("aabaabaa", "a", 8)  = 7
     * lastIndexOf("aabaabaa", "b", 8)  = 5
     * lastIndexOf("aabaabaa", "ab", 8) = 4
     * lastIndexOf("aabaabaa", "b", 9)  = 5
     * lastIndexOf("aabaabaa", "b", -1) = -1
     * lastIndexOf("aabaabaa", "a", 0)  = 0
     * lastIndexOf("aabaabaa", "b", 0)  = -1
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param startPos
     *            the start position, negative treated as zero
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static int lastIndexOf(String str, String searchStr, int startPos) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        return str.lastIndexOf(searchStr, startPos);
    }

    /**
     * <p>
     * Case in-sensitive find of the last index within a String.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position returns <code>-1</code>. An empty ("") search String always
     * matches unless the start position is negative. A start position greater
     * than the string length searches the whole string.
     * </p>
     * 
     * <pre>
     * lastIndexOfIgnoreCase(null, *)          = -1
     * lastIndexOfIgnoreCase(*, null)          = -1
     * lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
     * lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
     * lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.5
     */
    public static int lastIndexOfIgnoreCase(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        return lastIndexOfIgnoreCase(str, searchStr, str.length());
    }

    /**
     * <p>
     * Case in-sensitive find of the last index within a String from the
     * specified position.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A negative start
     * position returns <code>-1</code>. An empty ("") search String always
     * matches unless the start position is negative. A start position greater
     * than the string length searches the whole string.
     * </p>
     * 
     * <pre>
     * lastIndexOfIgnoreCase(null, *, *)          = -1
     * lastIndexOfIgnoreCase(*, null, *)          = -1
     * lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
     * lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
     * lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
     * lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
     * lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
     * lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
     * lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @param startPos
     *            the start position
     * @return the first index of the search String, -1 if no match or
     *         <code>null</code> string input
     * @since 2.5
     */
    public static int lastIndexOfIgnoreCase(String str, String searchStr,
            int startPos) {
        if (str == null || searchStr == null) {
            return INDEX_NOT_FOUND;
        }
        if (startPos > (str.length() - searchStr.length())) {
            startPos = str.length() - searchStr.length();
        }
        if (startPos < 0) {
            return INDEX_NOT_FOUND;
        }
        if (searchStr.length() == 0) {
            return startPos;
        }

        for (int i = startPos; i >= 0; i--) {
            if (str.regionMatches(true, i, searchStr, 0, searchStr.length())) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    // Contains
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if String contains a search character, handling <code>null</code>.
     * This method uses {@link String#indexOf(int)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * contains(null, *)    = false
     * contains("", *)      = false
     * contains("abc", 'a') = true
     * contains("abc", 'z') = false
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChar
     *            the character to find
     * @return true if the String contains the search character, false if not or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static boolean contains(String str, int searchChar) {
        if (isEmpty(str)) {
            return false;
        }
        return str.indexOf(searchChar) >= 0;
    }

    /**
     * <p>
     * Checks if String contains a search String, handling <code>null</code>.
     * This method uses {@link String#indexOf(String)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * contains(null, *)     = false
     * contains(*, null)     = false
     * contains("", "")      = true
     * contains("abc", "")   = true
     * contains("abc", "a")  = true
     * contains("abc", "z")  = false
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return true if the String contains the search String, false if not or
     *         <code>null</code> string input
     * @since 2.0
     */
    public static boolean contains(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return false;
        }
        return str.indexOf(searchStr) >= 0;
    }

    /**
     * <p>
     * Checks if String contains a search String irrespective of case, handling
     * <code>null</code>. Case-insensitivity is defined as by
     * {@link String#equalsIgnoreCase(String)}.
     * 
     * <p>
     * A <code>null</code> String will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * contains(null, *) = false
     * contains(*, null) = false
     * contains("", "") = true
     * contains("abc", "") = true
     * contains("abc", "a") = true
     * contains("abc", "z") = false
     * contains("abc", "A") = true
     * contains("abc", "Z") = false
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStr
     *            the String to find, may be null
     * @return true if the String contains the search String irrespective of
     *         case or false if not or <code>null</code> string input
     */
    public static boolean containsIgnoreCase(String str, String searchStr) {
        if (str == null || searchStr == null) {
            return false;
        }
        int len = searchStr.length();
        int max = str.length() - len;
        for (int i = 0; i <= max; i++) {
            if (str.regionMatches(true, i, searchStr, 0, len)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check whether the given String contains any whitespace characters.
     * 
     * @param str
     *            the String to check (may be <code>null</code>)
     * @return <code>true</code> if the String is not empty and contains at
     *         least 1 whitespace character
     * @see java.lang.Character#isWhitespace
     * @since 3.0
     */
    // From org.springframework.util.StringUtils, under Apache License 2.0
    public static boolean containsWhitespace(String str) {
        if (isEmpty(str)) {
            return false;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }

    // IndexOfAny chars
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Search a CharSequence to find the first index of any character in the
     * given set of characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A
     * <code>null</code> or zero length search array will return <code>-1</code>
     * .
     * </p>
     * 
     * <pre>
     * indexOfAny(null, *)                = -1
     * indexOfAny("", *)                  = -1
     * indexOfAny(*, null)                = -1
     * indexOfAny(*, [])                  = -1
     * indexOfAny("zzabyycdxx",['z','a']) = 0
     * indexOfAny("zzabyycdxx",['b','y']) = 3
     * indexOfAny("aba", ['z'])           = -1
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the index of any of the chars, -1 if no match or null input
     * @since 2.0
     * @since 3.0 Changed signature from indexOfAny(String, char[]) to
     *        indexOfAny(CharSequence, char[])
     */
    public static int indexOfAny(CharSequence cs, char[] searchChars) {
        if (isEmpty(cs) || isEmpty(searchChars)) {
            return INDEX_NOT_FOUND;
        }
        int csLen = cs.length();
        int csLast = csLen - 1;
        int searchLen = searchChars.length;
        int searchLast = searchLen - 1;
        for (int i = 0; i < csLen; i++) {
            char ch = cs.charAt(i);
            for (int j = 0; j < searchLen; j++) {
                if (searchChars[j] == ch) {
                    if (i < csLast && j < searchLast
                            && Character.isHighSurrogate(ch)) {
                        // ch is a supplementary character
                        if (searchChars[j + 1] == cs.charAt(i + 1)) {
                            return i;
                        }
                    } else {
                        return i;
                    }
                }
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Search a CharSequence to find the first index of any character in the
     * given set of characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A
     * <code>null</code> search string will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * indexOfAny(null, *)            = -1
     * indexOfAny("", *)              = -1
     * indexOfAny(*, null)            = -1
     * indexOfAny(*, "")              = -1
     * indexOfAny("zzabyycdxx", "za") = 0
     * indexOfAny("zzabyycdxx", "by") = 3
     * indexOfAny("aba","z")          = -1
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the index of any of the chars, -1 if no match or null input
     * @since 2.0
     * @since 3.0 Changed signature from indexOfAny(String, String) to
     *        indexOfAny(CharSequence, String)
     */
    public static int indexOfAny(CharSequence cs, String searchChars) {
        if (isEmpty(cs) || isEmpty(searchChars)) {
            return INDEX_NOT_FOUND;
        }
        return indexOfAny(cs, searchChars.toCharArray());
    }

    // ContainsAny
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if the CharSequence contains any character in the given set of
     * characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>false</code>. A
     * <code>null</code> or zero length search array will return
     * <code>false</code>.
     * </p>
     * 
     * <pre>
     * containsAny(null, *)                = false
     * containsAny("", *)                  = false
     * containsAny(*, null)                = false
     * containsAny(*, [])                  = false
     * containsAny("zzabyycdxx",['z','a']) = true
     * containsAny("zzabyycdxx",['b','y']) = true
     * containsAny("aba", ['z'])           = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the <code>true</code> if any of the chars are found,
     *         <code>false</code> if no match or null input
     * @since 2.4
     */
    public static boolean containsAny(String cs, char[] searchChars) {
        if (isEmpty(cs) || isEmpty(searchChars)) {
            return false;
        }
        int csLength = cs.length();
        int searchLength = searchChars.length;
        int csLast = csLength - 1;
        int searchLast = searchLength - 1;
        for (int i = 0; i < csLength; i++) {
            char ch = cs.charAt(i);
            for (int j = 0; j < searchLength; j++) {
                if (searchChars[j] == ch) {
                    if (Character.isHighSurrogate(ch)) {
                        if (j == searchLast) {
                            // missing low surrogate, fine, like
                            // String.indexOf(String)
                            return true;
                        }
                        if (i < csLast
                                && searchChars[j + 1] == cs.charAt(i + 1)) {
                            return true;
                        }
                    } else {
                        // ch is in the Basic Multilingual Plane
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * <p>
     * Checks if the CharSequence contains any character in the given set of
     * characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>false</code>. A
     * <code>null</code> search CharSequence will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * containsAny(null, *)            = false
     * containsAny("", *)              = false
     * containsAny(*, null)            = false
     * containsAny(*, "")              = false
     * containsAny("zzabyycdxx", "za") = true
     * containsAny("zzabyycdxx", "by") = true
     * containsAny("aba","z")          = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the <code>true</code> if any of the chars are found,
     *         <code>false</code> if no match or null input
     * @since 2.4
     */
    public static boolean containsAny(String cs, String searchChars) {
        if (searchChars == null) {
            return false;
        }
        return containsAny(cs, searchChars.toCharArray());
    }

    // IndexOfAnyBut chars
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Searches a CharSequence to find the first index of any character not in
     * the given set of characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>-1</code>. A
     * <code>null</code> or zero length search array will return <code>-1</code>
     * .
     * </p>
     * 
     * <pre>
     * indexOfAnyBut(null, *)           = -1
     * indexOfAnyBut("", *)             = -1
     * indexOfAnyBut(*, null)           = -1
     * indexOfAnyBut(*, [])             = -1
     * indexOfAnyBut("zzabyycdxx",'za') = 3
     * indexOfAnyBut("zzabyycdxx", '')  = 0
     * indexOfAnyBut("aba", 'ab')       = -1
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the index of any of the chars, -1 if no match or null input
     * @since 2.0
     * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to
     *        indexOfAnyBut(CharSequence, char[])
     */
    public static int indexOfAnyBut(CharSequence cs, char[] searchChars) {
        if (isEmpty(cs) || isEmpty(searchChars)) {
            return INDEX_NOT_FOUND;
        }
        int csLen = cs.length();
        int csLast = csLen - 1;
        int searchLen = searchChars.length;
        int searchLast = searchLen - 1;
        outer: for (int i = 0; i < csLen; i++) {
            char ch = cs.charAt(i);
            for (int j = 0; j < searchLen; j++) {
                if (searchChars[j] == ch) {
                    if (i < csLast && j < searchLast
                            && Character.isHighSurrogate(ch)) {
                        if (searchChars[j + 1] == cs.charAt(i + 1)) {
                            continue outer;
                        }
                    } else {
                        continue outer;
                    }
                }
            }
            return i;
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Search a String to find the first index of any character not in the given
     * set of characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A
     * <code>null</code> search string will return <code>-1</code>.
     * </p>
     * 
     * <pre>
     * indexOfAnyBut(null, *)            = -1
     * indexOfAnyBut("", *)              = -1
     * indexOfAnyBut(*, null)            = -1
     * indexOfAnyBut(*, "")              = -1
     * indexOfAnyBut("zzabyycdxx", "za") = 3
     * indexOfAnyBut("zzabyycdxx", "")   = 0
     * indexOfAnyBut("aba","ab")         = -1
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchChars
     *            the chars to search for, may be null
     * @return the index of any of the chars, -1 if no match or null input
     * @since 2.0
     */
    public static int indexOfAnyBut(String str, String searchChars) {
        if (isEmpty(str) || isEmpty(searchChars)) {
            return INDEX_NOT_FOUND;
        }
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            char ch = str.charAt(i);
            boolean chFound = searchChars.indexOf(ch) >= 0;
            if (i + 1 < strLen && Character.isHighSurrogate(ch)) {
                char ch2 = str.charAt(i + 1);
                if (chFound && searchChars.indexOf(ch2) < 0) {
                    return i;
                }
            } else {
                if (!chFound) {
                    return i;
                }
            }
        }
        return INDEX_NOT_FOUND;
    }

    // ContainsOnly
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if the CharSequence contains only certain characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>false</code>. A
     * <code>null</code> valid character array will return <code>false</code>.
     * An empty CharSequence (length()=0) always returns <code>true</code>.
     * </p>
     * 
     * <pre>
     * containsOnly(null, *)       = false
     * containsOnly(*, null)       = false
     * containsOnly("", *)         = true
     * containsOnly("ab", '')      = false
     * containsOnly("abab", 'abc') = true
     * containsOnly("ab1", 'abc')  = false
     * containsOnly("abz", 'abc')  = false
     * </pre>
     * 
     * @param cs
     *            the String to check, may be null
     * @param valid
     *            an array of valid chars, may be null
     * @return true if it only contains valid chars and is non-null
     * @since 3.0 Changed signature from containsOnly(String, char[]) to
     *        containsOnly(CharSequence, char[])
     */
    public static boolean containsOnly(CharSequence cs, char[] valid) {
        // All these pre-checks are to maintain API with an older version
        if (valid == null || cs == null) {
            return false;
        }
        if (cs.length() == 0) {
            return true;
        }
        if (valid.length == 0) {
            return false;
        }
        return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only certain characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>false</code>. A
     * <code>null</code> valid character String will return <code>false</code>.
     * An empty String (length()=0) always returns <code>true</code>.
     * </p>
     * 
     * <pre>
     * containsOnly(null, *)       = false
     * containsOnly(*, null)       = false
     * containsOnly("", *)         = true
     * containsOnly("ab", "")      = false
     * containsOnly("abab", "abc") = true
     * containsOnly("ab1", "abc")  = false
     * containsOnly("abz", "abc")  = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param validChars
     *            a String of valid chars, may be null
     * @return true if it only contains valid chars and is non-null
     * @since 2.0
     * @since 3.0 Changed signature from containsOnly(String, String) to
     *        containsOnly(CharSequence, String)
     */
    public static boolean containsOnly(CharSequence cs, String validChars) {
        if (cs == null || validChars == null) {
            return false;
        }
        return containsOnly(cs, validChars.toCharArray());
    }

    // ContainsNone
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks that the CharSequence does not contain certain characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>true</code>. A
     * <code>null</code> invalid character array will return <code>true</code>.
     * An empty CharSequence (length()=0) always returns true.
     * </p>
     * 
     * <pre>
     * containsNone(null, *)       = true
     * containsNone(*, null)       = true
     * containsNone("", *)         = true
     * containsNone("ab", '')      = true
     * containsNone("abab", 'xyz') = true
     * containsNone("ab1", 'xyz')  = true
     * containsNone("abz", 'xyz')  = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param searchChars
     *            an array of invalid chars, may be null
     * @return true if it contains none of the invalid chars, or is null
     * @since 2.0
     * @since 3.0 Changed signature from containsNone(String, char[]) to
     *        containsNone(CharSequence, char[])
     */
    public static boolean containsNone(CharSequence cs, char[] searchChars) {
        if (cs == null || searchChars == null) {
            return true;
        }
        int csLen = cs.length();
        int csLast = csLen - 1;
        int searchLen = searchChars.length;
        int searchLast = searchLen - 1;
        for (int i = 0; i < csLen; i++) {
            char ch = cs.charAt(i);
            for (int j = 0; j < searchLen; j++) {
                if (searchChars[j] == ch) {
                    if (Character.isHighSurrogate(ch)) {
                        if (j == searchLast) {
                            // missing low surrogate, fine, like
                            // String.indexOf(String)
                            return false;
                        }
                        if (i < csLast
                                && searchChars[j + 1] == cs.charAt(i + 1)) {
                            return false;
                        }
                    } else {
                        // ch is in the Basic Multilingual Plane
                        return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks that the CharSequence does not contain certain characters.
     * </p>
     * 
     * <p>
     * A <code>null</code> CharSequence will return <code>true</code>. A
     * <code>null</code> invalid character array will return <code>true</code>.
     * An empty String ("") always returns true.
     * </p>
     * 
     * <pre>
     * containsNone(null, *)       = true
     * containsNone(*, null)       = true
     * containsNone("", *)         = true
     * containsNone("ab", "")      = true
     * containsNone("abab", "xyz") = true
     * containsNone("ab1", "xyz")  = true
     * containsNone("abz", "xyz")  = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @param invalidChars
     *            a String of invalid chars, may be null
     * @return true if it contains none of the invalid chars, or is null
     * @since 2.0
     * @since 3.0 Changed signature from containsNone(String, String) to
     *        containsNone(CharSequence, String)
     */
    public static boolean containsNone(CharSequence cs, String invalidChars) {
        if (cs == null || invalidChars == null) {
            return true;
        }
        return containsNone(cs, invalidChars.toCharArray());
    }

    // IndexOfAny strings
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Find the first index of any of a set of potential substrings.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A
     * <code>null</code> or zero length search array will return <code>-1</code>
     * . A <code>null</code> search array entry will be ignored, but a search
     * array containing "" will return <code>0</code> if <code>str</code> is not
     * null. This method uses {@link String#indexOf(String)}.
     * </p>
     * 
     * <pre>
     * indexOfAny(null, *)                     = -1
     * indexOfAny(*, null)                     = -1
     * indexOfAny(*, [])                       = -1
     * indexOfAny("zzabyycdxx", ["ab","cd"])   = 2
     * indexOfAny("zzabyycdxx", ["cd","ab"])   = 2
     * indexOfAny("zzabyycdxx", ["mn","op"])   = -1
     * indexOfAny("zzabyycdxx", ["zab","aby"]) = 1
     * indexOfAny("zzabyycdxx", [""])          = 0
     * indexOfAny("", [""])                    = 0
     * indexOfAny("", ["a"])                   = -1
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStrs
     *            the Strings to search for, may be null
     * @return the first index of any of the searchStrs in str, -1 if no match
     */
    public static int indexOfAny(String str, String[] searchStrs) {
        if (str == null || searchStrs == null) {
            return INDEX_NOT_FOUND;
        }
        int sz = searchStrs.length;

        // String's can't have a MAX_VALUEth index.
        int ret = Integer.MAX_VALUE;

        int tmp = 0;
        for (int i = 0; i < sz; i++) {
            String search = searchStrs[i];
            if (search == null) {
                continue;
            }
            tmp = str.indexOf(search);
            if (tmp == INDEX_NOT_FOUND) {
                continue;
            }

            if (tmp < ret) {
                ret = tmp;
            }
        }

        return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret;
    }

    /**
     * <p>
     * Find the latest index of any of a set of potential substrings.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>-1</code>. A
     * <code>null</code> search array will return <code>-1</code>. A
     * <code>null</code> or zero length search array entry will be ignored, but
     * a search array containing "" will return the length of <code>str</code>
     * if <code>str</code> is not null. This method uses
     * {@link String#indexOf(String)}
     * </p>
     * 
     * <pre>
     * lastIndexOfAny(null, *)                   = -1
     * lastIndexOfAny(*, null)                   = -1
     * lastIndexOfAny(*, [])                     = -1
     * lastIndexOfAny(*, [null])                 = -1
     * lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6
     * lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6
     * lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
     * lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
     * lastIndexOfAny("zzabyycdxx", ["mn",""])   = 10
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param searchStrs
     *            the Strings to search for, may be null
     * @return the last index of any of the Strings, -1 if no match
     */
    public static int lastIndexOfAny(String str, String[] searchStrs) {
        if (str == null || searchStrs == null) {
            return INDEX_NOT_FOUND;
        }
        int sz = searchStrs.length;
        int ret = INDEX_NOT_FOUND;
        int tmp = 0;
        for (int i = 0; i < sz; i++) {
            String search = searchStrs[i];
            if (search == null) {
                continue;
            }
            tmp = str.lastIndexOf(search);
            if (tmp > ret) {
                ret = tmp;
            }
        }
        return ret;
    }

    // Substring
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets a substring from the specified String avoiding exceptions.
     * </p>
     * 
     * <p>
     * A negative start position can be used to start <code>n</code> characters
     * from the end of the String.
     * </p>
     * 
     * <p>
     * A <code>null</code> String will return <code>null</code>. An empty ("")
     * String will return "".
     * </p>
     * 
     * <pre>
     * substring(null, *)   = null
     * substring("", *)     = ""
     * substring("abc", 0)  = "abc"
     * substring("abc", 2)  = "c"
     * substring("abc", 4)  = ""
     * substring("abc", -2) = "bc"
     * substring("abc", -4) = "abc"
     * </pre>
     * 
     * @param str
     *            the String to get the substring from, may be null
     * @param start
     *            the position to start from, negative means count back from the
     *            end of the String by this many characters
     * @return substring from start position, <code>null</code> if null String
     *         input
     */
    public static String substring(String str, int start) {
        if (str == null) {
            return null;
        }

        // handle negatives, which means last n characters
        if (start < 0) {
            start = str.length() + start; // remember start is negative
        }

        if (start < 0) {
            start = 0;
        }
        if (start > str.length()) {
            return EMPTY_STRING;
        }

        return str.substring(start);
    }

    /**
     * <p>
     * Gets a substring from the specified String avoiding exceptions.
     * </p>
     * 
     * <p>
     * A negative start position can be used to start/end <code>n</code>
     * characters from the end of the String.
     * </p>
     * 
     * <p>
     * The returned substring starts with the character in the
     * <code>start</code> position and ends before the <code>end</code>
     * position. All position counting is zero-based -- i.e., to start at the
     * beginning of the string use <code>start = 0</code>. Negative start and
     * end positions can be used to specify offsets relative to the end of the
     * String.
     * </p>
     * 
     * <p>
     * If <code>start</code> is not strictly to the left of <code>end</code>, ""
     * is returned.
     * </p>
     * 
     * <pre>
     * substring(null, *, *)    = null
     * substring("", * ,  *)    = "";
     * substring("abc", 0, 2)   = "ab"
     * substring("abc", 2, 0)   = ""
     * substring("abc", 2, 4)   = "c"
     * substring("abc", 4, 6)   = ""
     * substring("abc", 2, 2)   = ""
     * substring("abc", -2, -1) = "b"
     * substring("abc", -4, 2)  = "ab"
     * </pre>
     * 
     * @param str
     *            the String to get the substring from, may be null
     * @param start
     *            the position to start from, negative means count back from the
     *            end of the String by this many characters
     * @param end
     *            the position to end at (exclusive), negative means count back
     *            from the end of the String by this many characters
     * @return substring from start position to end positon, <code>null</code>
     *         if null String input
     */
    public static String substring(String str, int start, int end) {
        if (str == null) {
            return null;
        }

        // handle negatives
        if (end < 0) {
            end = str.length() + end; // remember end is negative
        }
        if (start < 0) {
            start = str.length() + start; // remember start is negative
        }

        // check length next
        if (end > str.length()) {
            end = str.length();
        }

        // if start is greater than end, return ""
        if (start > end) {
            return EMPTY_STRING;
        }

        if (start < 0) {
            start = 0;
        }
        if (end < 0) {
            end = 0;
        }

        return str.substring(start, end);
    }

    // Left/Right/Mid
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the leftmost <code>len</code> characters of a String.
     * </p>
     * 
     * <p>
     * If <code>len</code> characters are not available, or the String is
     * <code>null</code>, the String will be returned without an exception. An
     * exception is thrown if len is negative.
     * </p>
     * 
     * <pre>
     * left(null, *)    = null
     * left(*, -ve)     = ""
     * left("", *)      = ""
     * left("abc", 0)   = ""
     * left("abc", 2)   = "ab"
     * left("abc", 4)   = "abc"
     * </pre>
     * 
     * @param str
     *            the String to get the leftmost characters from, may be null
     * @param len
     *            the length of the required String, must be zero or positive
     * @return the leftmost characters, <code>null</code> if null String input
     */
    public static String left(String str, int len) {
        if (str == null) {
            return null;
        }
        if (len < 0) {
            return EMPTY_STRING;
        }
        if (str.length() <= len) {
            return str;
        }
        return str.substring(0, len);
    }

    /**
     * <p>
     * Gets the rightmost <code>len</code> characters of a String.
     * </p>
     * 
     * <p>
     * If <code>len</code> characters are not available, or the String is
     * <code>null</code>, the String will be returned without an an exception.
     * An exception is thrown if len is negative.
     * </p>
     * 
     * <pre>
     * right(null, *)    = null
     * right(*, -ve)     = ""
     * right("", *)      = ""
     * right("abc", 0)   = ""
     * right("abc", 2)   = "bc"
     * right("abc", 4)   = "abc"
     * </pre>
     * 
     * @param str
     *            the String to get the rightmost characters from, may be null
     * @param len
     *            the length of the required String, must be zero or positive
     * @return the rightmost characters, <code>null</code> if null String input
     */
    public static String right(String str, int len) {
        if (str == null) {
            return null;
        }
        if (len < 0) {
            return EMPTY_STRING;
        }
        if (str.length() <= len) {
            return str;
        }
        return str.substring(str.length() - len);
    }

    /**
     * <p>
     * Gets <code>len</code> characters from the middle of a String.
     * </p>
     * 
     * <p>
     * If <code>len</code> characters are not available, the remainder of the
     * String will be returned without an exception. If the String is
     * <code>null</code>, <code>null</code> will be returned. An exception is
     * thrown if len is negative.
     * </p>
     * 
     * <pre>
     * mid(null, *, *)    = null
     * mid(*, *, -ve)     = ""
     * mid("", 0, *)      = ""
     * mid("abc", 0, 2)   = "ab"
     * mid("abc", 0, 4)   = "abc"
     * mid("abc", 2, 4)   = "c"
     * mid("abc", 4, 2)   = ""
     * mid("abc", -2, 2)  = "ab"
     * </pre>
     * 
     * @param str
     *            the String to get the characters from, may be null
     * @param pos
     *            the position to start from, negative treated as zero
     * @param len
     *            the length of the required String, must be zero or positive
     * @return the middle characters, <code>null</code> if null String input
     */
    public static String mid(String str, int pos, int len) {
        if (str == null) {
            return null;
        }
        if (len < 0 || pos > str.length()) {
            return EMPTY_STRING;
        }
        if (pos < 0) {
            pos = 0;
        }
        if (str.length() <= (pos + len)) {
            return str.substring(pos);
        }
        return str.substring(pos, pos + len);
    }

    // SubStringAfter/SubStringBefore
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the substring before the first occurrence of a separator. The
     * separator is not returned.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input will return <code>null</code>. An empty
     * ("") string input will return the empty string. A <code>null</code>
     * separator will return the input string.
     * </p>
     * 
     * <p>
     * If nothing is found, the string input is returned.
     * </p>
     * 
     * <pre>
     * substringBefore(null, *)      = null
     * substringBefore("", *)        = ""
     * substringBefore("abc", "a")   = ""
     * substringBefore("abcba", "b") = "a"
     * substringBefore("abc", "c")   = "ab"
     * substringBefore("abc", "d")   = "abc"
     * substringBefore("abc", "")    = ""
     * substringBefore("abc", null)  = "abc"
     * </pre>
     * 
     * @param str
     *            the String to get a substring from, may be null
     * @param separator
     *            the String to search for, may be null
     * @return the substring before the first occurrence of the separator,
     *         <code>null</code> if null String input
     * @since 2.0
     */
    public static String substringBefore(String str, String separator) {
        if (isEmpty(str) || separator == null) {
            return str;
        }
        if (separator.length() == 0) {
            return EMPTY_STRING;
        }
        int pos = str.indexOf(separator);
        if (pos == INDEX_NOT_FOUND) {
            return str;
        }
        return str.substring(0, pos);
    }

    /**
     * <p>
     * Gets the substring after the first occurrence of a separator. The
     * separator is not returned.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input will return <code>null</code>. An empty
     * ("") string input will return the empty string. A <code>null</code>
     * separator will return the empty string if the input string is not
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * If nothing is found, the empty string is returned.
     * </p>
     * 
     * <pre>
     * substringAfter(null, *)      = null
     * substringAfter("", *)        = ""
     * substringAfter(*, null)      = ""
     * substringAfter("abc", "a")   = "bc"
     * substringAfter("abcba", "b") = "cba"
     * substringAfter("abc", "c")   = ""
     * substringAfter("abc", "d")   = ""
     * substringAfter("abc", "")    = "abc"
     * </pre>
     * 
     * @param str
     *            the String to get a substring from, may be null
     * @param separator
     *            the String to search for, may be null
     * @return the substring after the first occurrence of the separator,
     *         <code>null</code> if null String input
     * @since 2.0
     */
    public static String substringAfter(String str, String separator) {
        if (isEmpty(str)) {
            return str;
        }
        if (separator == null) {
            return EMPTY_STRING;
        }
        int pos = str.indexOf(separator);
        if (pos == INDEX_NOT_FOUND) {
            return EMPTY_STRING;
        }
        return str.substring(pos + separator.length());
    }

    /**
     * <p>
     * Gets the substring before the last occurrence of a separator. The
     * separator is not returned.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input will return <code>null</code>. An empty
     * ("") string input will return the empty string. An empty or
     * <code>null</code> separator will return the input string.
     * </p>
     * 
     * <p>
     * If nothing is found, the string input is returned.
     * </p>
     * 
     * <pre>
     * substringBeforeLast(null, *)      = null
     * substringBeforeLast("", *)        = ""
     * substringBeforeLast("abcba", "b") = "abc"
     * substringBeforeLast("abc", "c")   = "ab"
     * substringBeforeLast("a", "a")     = ""
     * substringBeforeLast("a", "z")     = "a"
     * substringBeforeLast("a", null)    = "a"
     * substringBeforeLast("a", "")      = "a"
     * </pre>
     * 
     * @param str
     *            the String to get a substring from, may be null
     * @param separator
     *            the String to search for, may be null
     * @return the substring before the last occurrence of the separator,
     *         <code>null</code> if null String input
     * @since 2.0
     */
    public static String substringBeforeLast(String str, String separator) {
        if (isEmpty(str) || isEmpty(separator)) {
            return str;
        }
        int pos = str.lastIndexOf(separator);
        if (pos == INDEX_NOT_FOUND) {
            return str;
        }
        return str.substring(0, pos);
    }

    /**
     * <p>
     * Gets the substring after the last occurrence of a separator. The
     * separator is not returned.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input will return <code>null</code>. An empty
     * ("") string input will return the empty string. An empty or
     * <code>null</code> separator will return the empty string if the input
     * string is not <code>null</code>.
     * </p>
     * 
     * <p>
     * If nothing is found, the empty string is returned.
     * </p>
     * 
     * <pre>
     * substringAfterLast(null, *)      = null
     * substringAfterLast("", *)        = ""
     * substringAfterLast(*, "")        = ""
     * substringAfterLast(*, null)      = ""
     * substringAfterLast("abc", "a")   = "bc"
     * substringAfterLast("abcba", "b") = "a"
     * substringAfterLast("abc", "c")   = ""
     * substringAfterLast("a", "a")     = ""
     * substringAfterLast("a", "z")     = ""
     * </pre>
     * 
     * @param str
     *            the String to get a substring from, may be null
     * @param separator
     *            the String to search for, may be null
     * @return the substring after the last occurrence of the separator,
     *         <code>null</code> if null String input
     * @since 2.0
     */
    public static String substringAfterLast(String str, String separator) {
        if (isEmpty(str)) {
            return str;
        }
        if (isEmpty(separator)) {
            return EMPTY_STRING;
        }
        int pos = str.lastIndexOf(separator);
        if (pos == INDEX_NOT_FOUND
                || pos == (str.length() - separator.length())) {
            return EMPTY_STRING;
        }
        return str.substring(pos + separator.length());
    }

    // Substring between
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the String that is nested in between two instances of the same
     * String.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> tag returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * substringBetween(null, *)            = null
     * substringBetween("", "")             = ""
     * substringBetween("", "tag")          = null
     * substringBetween("tagabctag", null)  = null
     * substringBetween("tagabctag", "")    = ""
     * substringBetween("tagabctag", "tag") = "abc"
     * </pre>
     * 
     * @param str
     *            the String containing the substring, may be null
     * @param tag
     *            the String before and after the substring, may be null
     * @return the substring, <code>null</code> if no match
     * @since 2.0
     */
    public static String substringBetween(String str, String tag) {
        return substringBetween(str, tag, tag);
    }

    /**
     * <p>
     * Gets the String that is nested in between two Strings. Only the first
     * match is returned.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> open/close returns <code>null</code> (no match). An
     * empty ("") open and close returns an empty string.
     * </p>
     * 
     * <pre>
     * substringBetween("wx[b]yz", "[", "]") = "b"
     * substringBetween(null, *, *)          = null
     * substringBetween(*, null, *)          = null
     * substringBetween(*, *, null)          = null
     * substringBetween("", "", "")          = ""
     * substringBetween("", "", "]")         = null
     * substringBetween("", "[", "]")        = null
     * substringBetween("yabcz", "", "")     = ""
     * substringBetween("yabcz", "y", "z")   = "abc"
     * substringBetween("yabczyabcz", "y", "z")   = "abc"
     * </pre>
     * 
     * @param str
     *            the String containing the substring, may be null
     * @param open
     *            the String before the substring, may be null
     * @param close
     *            the String after the substring, may be null
     * @return the substring, <code>null</code> if no match
     * @since 2.0
     */
    public static String substringBetween(String str, String open, String close) {
        if (str == null || open == null || close == null) {
            return null;
        }
        int start = str.indexOf(open);
        if (start != INDEX_NOT_FOUND) {
            int end = str.indexOf(close, start + open.length());
            if (end != INDEX_NOT_FOUND) {
                return str.substring(start + open.length(), end);
            }
        }
        return null;
    }

    /**
     * <p>
     * Searches a String for substrings delimited by a start and end tag,
     * returning all matching substrings in an array.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> open/close returns <code>null</code> (no match). An
     * empty ("") open/close returns <code>null</code> (no match).
     * </p>
     * 
     * <pre>
     * substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
     * substringsBetween(null, *, *)            = null
     * substringsBetween(*, null, *)            = null
     * substringsBetween(*, *, null)            = null
     * substringsBetween("", "[", "]")          = []
     * </pre>
     * 
     * @param str
     *            the String containing the substrings, null returns null, empty
     *            returns empty
     * @param open
     *            the String identifying the start of the substring, empty
     *            returns null
     * @param close
     *            the String identifying the end of the substring, empty returns
     *            null
     * @return a String Array of substrings, or <code>null</code> if no match
     * @since 2.3
     */
    public static String[] substringsBetween(String str, String open,
            String close) {
        if (str == null || isEmpty(open) || isEmpty(close)) {
            return null;
        }
        int strLen = str.length();
        if (strLen == 0) {
            return EMPTY_STRING_ARRAY;
        }
        int closeLen = close.length();
        int openLen = open.length();
        List<String> list = new ArrayList<String>();
        int pos = 0;
        while (pos < (strLen - closeLen)) {
            int start = str.indexOf(open, pos);
            if (start < 0) {
                break;
            }
            start += openLen;
            int end = str.indexOf(close, start);
            if (end < 0) {
                break;
            }
            list.add(str.substring(start, end));
            pos = end + closeLen;
        }
        if (list.isEmpty()) {
            return null;
        }
        return list.toArray(new String[list.size()]);
    }

    // Nested extraction
    // -----------------------------------------------------------------------

    // Splitting
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Splits the provided text into an array, using whitespace as the
     * separator. Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as one separator. For more control over the split
     * use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * split(null)       = null
     * split("")         = []
     * split("abc def")  = ["abc", "def"]
     * split("abc  def") = ["abc", "def"]
     * split(" abc ")    = ["abc"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     */
    public static String[] split(String str) {
        return split(str, null, -1);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator specified. This is an
     * alternative to using StringTokenizer.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as one separator. For more control over the split
     * use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * split(null, *)         = null
     * split("", *)           = []
     * split("a.b.c", '.')    = ["a", "b", "c"]
     * split("a..b.c", '.')   = ["a", "b", "c"]
     * split("a:b:c", '.')    = ["a:b:c"]
     * split("a b c", ' ')    = ["a", "b", "c"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separatorChar
     *            the character used as the delimiter
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.0
     */
    public static String[] split(String str, char separatorChar) {
        return splitWorker(str, separatorChar, false);
    }

    /**
     * <p>
     * Splits the provided text into an array, separators specified. This is an
     * alternative to using StringTokenizer.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as one separator. For more control over the split
     * use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separatorChars splits on whitespace.
     * </p>
     * 
     * <pre>
     * split(null, *)         = null
     * split("", *)           = []
     * split("abc def", null) = ["abc", "def"]
     * split("abc def", " ")  = ["abc", "def"]
     * split("abc  def", " ") = ["abc", "def"]
     * split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separatorChars
     *            the characters used as the delimiters, <code>null</code>
     *            splits on whitespace
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     */
    public static String[] split(String str, String separatorChars) {
        return splitWorker(str, separatorChars, -1, false);
    }

    /**
     * <p>
     * Splits the provided text into an array with a maximum length, separators
     * specified.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as one separator.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separatorChars splits on whitespace.
     * </p>
     * 
     * <p>
     * If more than <code>max</code> delimited substrings are found, the last
     * returned string includes all characters after the first
     * <code>max - 1</code> returned strings (including separator characters).
     * </p>
     * 
     * <pre>
     * split(null, *, *)            = null
     * split("", *, *)              = []
     * split("ab de fg", null, 0)   = ["ab", "cd", "ef"]
     * split("ab   de fg", null, 0) = ["ab", "cd", "ef"]
     * split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
     * split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separatorChars
     *            the characters used as the delimiters, <code>null</code>
     *            splits on whitespace
     * @param max
     *            the maximum number of elements to include in the array. A zero
     *            or negative value implies no limit
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     */
    public static String[] split(String str, String separatorChars, int max) {
        return splitWorker(str, separatorChars, max, false);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator string specified.
     * </p>
     * 
     * <p>
     * The separator(s) will not be included in the returned String array.
     * Adjacent separators are treated as one separator.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separator splits on whitespace.
     * </p>
     * 
     * <pre>
     * splitByWholeSeparator(null, *)               = null
     * splitByWholeSeparator("", *)                 = []
     * splitByWholeSeparator("ab de fg", null)      = ["ab", "de", "fg"]
     * splitByWholeSeparator("ab   de fg", null)    = ["ab", "de", "fg"]
     * splitByWholeSeparator("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
     * splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separator
     *            String containing the String to be used as a delimiter,
     *            <code>null</code> splits on whitespace
     * @return an array of parsed Strings, <code>null</code> if null String was
     *         input
     */
    public static String[] splitByWholeSeparator(String str, String separator) {
        return splitByWholeSeparatorWorker(str, separator, -1, false);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator string specified.
     * Returns a maximum of <code>max</code> substrings.
     * </p>
     * 
     * <p>
     * The separator(s) will not be included in the returned String array.
     * Adjacent separators are treated as one separator.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separator splits on whitespace.
     * </p>
     * 
     * <pre>
     * splitByWholeSeparator(null, *, *)               = null
     * splitByWholeSeparator("", *, *)                 = []
     * splitByWholeSeparator("ab de fg", null, 0)      = ["ab", "de", "fg"]
     * splitByWholeSeparator("ab   de fg", null, 0)    = ["ab", "de", "fg"]
     * splitByWholeSeparator("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
     * splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
     * splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separator
     *            String containing the String to be used as a delimiter,
     *            <code>null</code> splits on whitespace
     * @param max
     *            the maximum number of elements to include in the returned
     *            array. A zero or negative value implies no limit.
     * @return an array of parsed Strings, <code>null</code> if null String was
     *         input
     */
    public static String[] splitByWholeSeparator(String str, String separator,
            int max) {
        return splitByWholeSeparatorWorker(str, separator, max, false);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator string specified.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. For more control
     * over the split use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separator splits on whitespace.
     * </p>
     * 
     * <pre>
     * splitByWholeSeparatorPreserveAllTokens(null, *)               = null
     * splitByWholeSeparatorPreserveAllTokens("", *)                 = []
     * splitByWholeSeparatorPreserveAllTokens("ab de fg", null)      = ["ab", "de", "fg"]
     * splitByWholeSeparatorPreserveAllTokens("ab   de fg", null)    = ["ab", "", "", "de", "fg"]
     * splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
     * splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separator
     *            String containing the String to be used as a delimiter,
     *            <code>null</code> splits on whitespace
     * @return an array of parsed Strings, <code>null</code> if null String was
     *         input
     * @since 2.4
     */
    public static String[] splitByWholeSeparatorPreserveAllTokens(String str,
            String separator) {
        return splitByWholeSeparatorWorker(str, separator, -1, true);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator string specified.
     * Returns a maximum of <code>max</code> substrings.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. For more control
     * over the split use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separator splits on whitespace.
     * </p>
     * 
     * <pre>
     * splitByWholeSeparatorPreserveAllTokens(null, *, *)               = null
     * splitByWholeSeparatorPreserveAllTokens("", *, *)                 = []
     * splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0)      = ["ab", "de", "fg"]
     * splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 0)    = ["ab", "", "", "de", "fg"]
     * splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
     * splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
     * splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be null
     * @param separator
     *            String containing the String to be used as a delimiter,
     *            <code>null</code> splits on whitespace
     * @param max
     *            the maximum number of elements to include in the returned
     *            array. A zero or negative value implies no limit.
     * @return an array of parsed Strings, <code>null</code> if null String was
     *         input
     * @since 2.4
     */
    public static String[] splitByWholeSeparatorPreserveAllTokens(String str,
            String separator, int max) {
        return splitByWholeSeparatorWorker(str, separator, max, true);
    }

    /**
     * Performs the logic for the
     * <code>splitByWholeSeparatorPreserveAllTokens</code> methods.
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separator
     *            String containing the String to be used as a delimiter,
     *            <code>null</code> splits on whitespace
     * @param max
     *            the maximum number of elements to include in the returned
     *            array. A zero or negative value implies no limit.
     * @param preserveAllTokens
     *            if <code>true</code>, adjacent separators are treated as empty
     *            token separators; if <code>false</code>, adjacent separators
     *            are treated as one separator.
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.4
     */
    private static String[] splitByWholeSeparatorWorker(String str,
            String separator, int max, boolean preserveAllTokens) {
        if (str == null) {
            return null;
        }

        int len = str.length();

        if (len == 0) {
            return EMPTY_STRING_ARRAY;
        }

        if ((separator == null) || (EMPTY_STRING.equals(separator))) {
            // Split on whitespace.
            return splitWorker(str, null, max, preserveAllTokens);
        }

        int separatorLength = separator.length();

        ArrayList<String> substrings = new ArrayList<String>();
        int numberOfSubstrings = 0;
        int beg = 0;
        int end = 0;
        while (end < len) {
            end = str.indexOf(separator, beg);

            if (end > -1) {
                if (end > beg) {
                    numberOfSubstrings += 1;

                    if (numberOfSubstrings == max) {
                        end = len;
                        substrings.add(str.substring(beg));
                    } else {
                        // The following is OK, because String.substring( beg,
                        // end ) excludes
                        // the character at the position 'end'.
                        substrings.add(str.substring(beg, end));

                        // Set the starting point for the next search.
                        // The following is equivalent to beg = end +
                        // (separatorLength - 1) + 1,
                        // which is the right calculation:
                        beg = end + separatorLength;
                    }
                } else {
                    // We found a consecutive occurrence of the separator, so
                    // skip it.
                    if (preserveAllTokens) {
                        numberOfSubstrings += 1;
                        if (numberOfSubstrings == max) {
                            end = len;
                            substrings.add(str.substring(beg));
                        } else {
                            substrings.add(EMPTY_STRING);
                        }
                    }
                    beg = end + separatorLength;
                }
            } else {
                // String.substring( beg ) goes from 'beg' to the end of the
                // String.
                substrings.add(str.substring(beg));
                end = len;
            }
        }

        return substrings.toArray(new String[substrings.size()]);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Splits the provided text into an array, using whitespace as the
     * separator, preserving all tokens, including empty tokens created by
     * adjacent separators. This is an alternative to using StringTokenizer.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. For more control
     * over the split use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * splitPreserveAllTokens(null)       = null
     * splitPreserveAllTokens("")         = []
     * splitPreserveAllTokens("abc def")  = ["abc", "def"]
     * splitPreserveAllTokens("abc  def") = ["abc", "", "def"]
     * splitPreserveAllTokens(" abc ")    = ["", "abc", ""]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.1
     */
    public static String[] splitPreserveAllTokens(String str) {
        return splitWorker(str, null, -1, true);
    }

    /**
     * <p>
     * Splits the provided text into an array, separator specified, preserving
     * all tokens, including empty tokens created by adjacent separators. This
     * is an alternative to using StringTokenizer.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. For more control
     * over the split use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * splitPreserveAllTokens(null, *)         = null
     * splitPreserveAllTokens("", *)           = []
     * splitPreserveAllTokens("a.b.c", '.')    = ["a", "b", "c"]
     * splitPreserveAllTokens("a..b.c", '.')   = ["a", "", "b", "c"]
     * splitPreserveAllTokens("a:b:c", '.')    = ["a:b:c"]
     * splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
     * splitPreserveAllTokens("a b c", ' ')    = ["a", "b", "c"]
     * splitPreserveAllTokens("a b c ", ' ')   = ["a", "b", "c", ""]
     * splitPreserveAllTokens("a b c  ", ' ')   = ["a", "b", "c", "", ""]
     * splitPreserveAllTokens(" a b c", ' ')   = ["", a", "b", "c"]
     * splitPreserveAllTokens("  a b c", ' ')  = ["", "", a", "b", "c"]
     * splitPreserveAllTokens(" a b c ", ' ')  = ["", a", "b", "c", ""]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separatorChar
     *            the character used as the delimiter, <code>null</code> splits
     *            on whitespace
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.1
     */
    public static String[] splitPreserveAllTokens(String str, char separatorChar) {
        return splitWorker(str, separatorChar, true);
    }

    /**
     * Performs the logic for the <code>split</code> and
     * <code>splitPreserveAllTokens</code> methods that do not return a maximum
     * array length.
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separatorChar
     *            the separate character
     * @param preserveAllTokens
     *            if <code>true</code>, adjacent separators are treated as empty
     *            token separators; if <code>false</code>, adjacent separators
     *            are treated as one separator.
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     */
    private static String[] splitWorker(String str, char separatorChar,
            boolean preserveAllTokens) {
        // Performance tuned for 2.0 (JDK1.4)

        if (str == null) {
            return null;
        }
        int len = str.length();
        if (len == 0) {
            return EMPTY_STRING_ARRAY;
        }
        List<String> list = new ArrayList<String>();
        int i = 0, start = 0;
        boolean match = false;
        boolean lastMatch = false;
        while (i < len) {
            if (str.charAt(i) == separatorChar) {
                if (match || preserveAllTokens) {
                    list.add(str.substring(start, i));
                    match = false;
                    lastMatch = true;
                }
                start = ++i;
                continue;
            }
            lastMatch = false;
            match = true;
            i++;
        }
        if (match || (preserveAllTokens && lastMatch)) {
            list.add(str.substring(start, i));
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * <p>
     * Splits the provided text into an array, separators specified, preserving
     * all tokens, including empty tokens created by adjacent separators. This
     * is an alternative to using StringTokenizer.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. For more control
     * over the split use the StrTokenizer class.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separatorChars splits on whitespace.
     * </p>
     * 
     * <pre>
     * splitPreserveAllTokens(null, *)           = null
     * splitPreserveAllTokens("", *)             = []
     * splitPreserveAllTokens("abc def", null)   = ["abc", "def"]
     * splitPreserveAllTokens("abc def", " ")    = ["abc", "def"]
     * splitPreserveAllTokens("abc  def", " ")   = ["abc", "", def"]
     * splitPreserveAllTokens("ab:cd:ef", ":")   = ["ab", "cd", "ef"]
     * splitPreserveAllTokens("ab:cd:ef:", ":")  = ["ab", "cd", "ef", ""]
     * splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
     * splitPreserveAllTokens("ab::cd:ef", ":")  = ["ab", "", cd", "ef"]
     * splitPreserveAllTokens(":cd:ef", ":")     = ["", cd", "ef"]
     * splitPreserveAllTokens("::cd:ef", ":")    = ["", "", cd", "ef"]
     * splitPreserveAllTokens(":cd:ef:", ":")    = ["", cd", "ef", ""]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separatorChars
     *            the characters used as the delimiters, <code>null</code>
     *            splits on whitespace
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.1
     */
    public static String[] splitPreserveAllTokens(String str,
            String separatorChars) {
        return splitWorker(str, separatorChars, -1, true);
    }

    /**
     * <p>
     * Splits the provided text into an array with a maximum length, separators
     * specified, preserving all tokens, including empty tokens created by
     * adjacent separators.
     * </p>
     * 
     * <p>
     * The separator is not included in the returned String array. Adjacent
     * separators are treated as separators for empty tokens. Adjacent
     * separators are treated as one separator.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>. A
     * <code>null</code> separatorChars splits on whitespace.
     * </p>
     * 
     * <p>
     * If more than <code>max</code> delimited substrings are found, the last
     * returned string includes all characters after the first
     * <code>max - 1</code> returned strings (including separator characters).
     * </p>
     * 
     * <pre>
     * splitPreserveAllTokens(null, *, *)            = null
     * splitPreserveAllTokens("", *, *)              = []
     * splitPreserveAllTokens("ab de fg", null, 0)   = ["ab", "cd", "ef"]
     * splitPreserveAllTokens("ab   de fg", null, 0) = ["ab", "cd", "ef"]
     * splitPreserveAllTokens("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
     * splitPreserveAllTokens("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
     * splitPreserveAllTokens("ab   de fg", null, 2) = ["ab", "  de fg"]
     * splitPreserveAllTokens("ab   de fg", null, 3) = ["ab", "", " de fg"]
     * splitPreserveAllTokens("ab   de fg", null, 4) = ["ab", "", "", "de fg"]
     * </pre>
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separatorChars
     *            the characters used as the delimiters, <code>null</code>
     *            splits on whitespace
     * @param max
     *            the maximum number of elements to include in the array. A zero
     *            or negative value implies no limit
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.1
     */
    public static String[] splitPreserveAllTokens(String str,
            String separatorChars, int max) {
        return splitWorker(str, separatorChars, max, true);
    }

    /**
     * Performs the logic for the <code>split</code> and
     * <code>splitPreserveAllTokens</code> methods that return a maximum array
     * length.
     * 
     * @param str
     *            the String to parse, may be <code>null</code>
     * @param separatorChars
     *            the separate character
     * @param max
     *            the maximum number of elements to include in the array. A zero
     *            or negative value implies no limit.
     * @param preserveAllTokens
     *            if <code>true</code>, adjacent separators are treated as empty
     *            token separators; if <code>false</code>, adjacent separators
     *            are treated as one separator.
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     */
    private static String[] splitWorker(String str, String separatorChars,
            int max, boolean preserveAllTokens) {
        // Performance tuned for 2.0 (JDK1.4)
        // Direct code is quicker than StringTokenizer.
        // Also, StringTokenizer uses isSpace() not isWhitespace()

        if (str == null) {
            return null;
        }
        int len = str.length();
        if (len == 0) {
            return EMPTY_STRING_ARRAY;
        }
        List<String> list = new ArrayList<String>();
        int sizePlus1 = 1;
        int i = 0, start = 0;
        boolean match = false;
        boolean lastMatch = false;
        if (separatorChars == null) {
            // Null separator means use whitespace
            while (i < len) {
                if (Character.isWhitespace(str.charAt(i))) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                    continue;
                }
                lastMatch = false;
                match = true;
                i++;
            }
        } else if (separatorChars.length() == 1) {
            // Optimise 1 character case
            char sep = separatorChars.charAt(0);
            while (i < len) {
                if (str.charAt(i) == sep) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                    continue;
                }
                lastMatch = false;
                match = true;
                i++;
            }
        } else {
            // standard case
            while (i < len) {
                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                    continue;
                }
                lastMatch = false;
                match = true;
                i++;
            }
        }
        if (match || (preserveAllTokens && lastMatch)) {
            list.add(str.substring(start, i));
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * <p>
     * Splits a String by Character type as returned by
     * <code>java.lang.Character.getType(char)</code>. Groups of contiguous
     * characters of the same type are returned as complete tokens.
     * 
     * <pre>
     * splitByCharacterType(null)         = null
     * splitByCharacterType("")           = []
     * splitByCharacterType("ab de fg")   = ["ab", " ", "de", " ", "fg"]
     * splitByCharacterType("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
     * splitByCharacterType("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
     * splitByCharacterType("number5")    = ["number", "5"]
     * splitByCharacterType("fooBar")     = ["foo", "B", "ar"]
     * splitByCharacterType("foo200Bar")  = ["foo", "200", "B", "ar"]
     * splitByCharacterType("ASFRules")   = ["ASFR", "ules"]
     * </pre>
     * 
     * @param str
     *            the String to split, may be <code>null</code>
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.4
     */
    public static String[] splitByCharacterType(String str) {
        return splitByCharacterType(str, false);
    }

    /**
     * <p>
     * Splits a String by Character type as returned by
     * <code>java.lang.Character.getType(char)</code>. Groups of contiguous
     * characters of the same type are returned as complete tokens, with the
     * following exception: the character of type
     * <code>Character.UPPERCASE_LETTER</code>, if any, immediately preceding a
     * token of type <code>Character.LOWERCASE_LETTER</code> will belong to the
     * following token rather than to the preceding, if any,
     * <code>Character.UPPERCASE_LETTER</code> token.
     * 
     * <pre>
     * splitByCharacterTypeCamelCase(null)         = null
     * splitByCharacterTypeCamelCase("")           = []
     * splitByCharacterTypeCamelCase("ab de fg")   = ["ab", " ", "de", " ", "fg"]
     * splitByCharacterTypeCamelCase("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
     * splitByCharacterTypeCamelCase("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
     * splitByCharacterTypeCamelCase("number5")    = ["number", "5"]
     * splitByCharacterTypeCamelCase("fooBar")     = ["foo", "Bar"]
     * splitByCharacterTypeCamelCase("foo200Bar")  = ["foo", "200", "Bar"]
     * splitByCharacterTypeCamelCase("ASFRules")   = ["ASF", "Rules"]
     * </pre>
     * 
     * @param str
     *            the String to split, may be <code>null</code>
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.4
     */
    public static String[] splitByCharacterTypeCamelCase(String str) {
        return splitByCharacterType(str, true);
    }

    /**
     * <p>
     * Splits a String by Character type as returned by
     * <code>java.lang.Character.getType(char)</code>. Groups of contiguous
     * characters of the same type are returned as complete tokens, with the
     * following exception: if <code>camelCase</code> is <code>true</code>, the
     * character of type <code>Character.UPPERCASE_LETTER</code>, if any,
     * immediately preceding a token of type
     * <code>Character.LOWERCASE_LETTER</code> will belong to the following
     * token rather than to the preceding, if any,
     * <code>Character.UPPERCASE_LETTER</code> token.
     * 
     * @param str
     *            the String to split, may be <code>null</code>
     * @param camelCase
     *            whether to use so-called "camel-case" for letter types
     * @return an array of parsed Strings, <code>null</code> if null String
     *         input
     * @since 2.4
     */
    private static String[] splitByCharacterType(String str, boolean camelCase) {
        if (str == null) {
            return null;
        }
        if (str.length() == 0) {
            return EMPTY_STRING_ARRAY;
        }
        char[] c = str.toCharArray();
        List<String> list = new ArrayList<String>();
        int tokenStart = 0;
        int currentType = Character.getType(c[tokenStart]);
        for (int pos = tokenStart + 1; pos < c.length; pos++) {
            int type = Character.getType(c[pos]);
            if (type == currentType) {
                continue;
            }
            if (camelCase && type == Character.LOWERCASE_LETTER
                    && currentType == Character.UPPERCASE_LETTER) {
                int newTokenStart = pos - 1;
                if (newTokenStart != tokenStart) {
                    list.add(new String(c, tokenStart, newTokenStart
                            - tokenStart));
                    tokenStart = newTokenStart;
                }
            } else {
                list.add(new String(c, tokenStart, pos - tokenStart));
                tokenStart = pos;
            }
            currentType = type;
        }
        list.add(new String(c, tokenStart, c.length - tokenStart));
        return list.toArray(new String[list.size()]);
    }

    // Joining
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Joins the provided elements into a single String.
     * </p>
     * 
     * <p>
     * No separator is added to the joined String. Null objects or empty string
     * elements are represented by empty strings.
     * </p>
     * 
     * <pre>
     * concat("a", "b", "c") = "abc"
     * concat(null, "", "a") = "a"
     * </pre>
     * 
     * @param elements
     *            the values to join together
     * @return the concatenated String
     * @since 3.0
     */
    public static String concat(Object... elements) {
        return join(elements, null);
    }

    /**
     * <p>
     * Joins the provided elements into a single String, with the specified
     * separator between each element.
     * </p>
     * 
     * <p>
     * No separator is added before or after the joined String. Null objects or
     * empty string elements are represented by empty strings.
     * </p>
     * 
     * <pre>
     * concatWith(".", "a", "b", "c") = "a.b.c"
     * concatWith("", null, "", "a") = "a"
     * </pre>
     * 
     * @param separator
     *            the value to put between elements
     * @param elements
     *            the values to join together
     * @return the concatenated String
     * @since 3.0
     */
    public static String concatWith(String separator, Object... elements) {
        return join(elements, separator);
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing
     * the provided list of elements.
     * </p>
     * 
     * <p>
     * No separator is added to the joined String. Null objects or empty strings
     * within the array are represented by empty strings.
     * </p>
     * 
     * <pre>
     * join(null)            = null
     * join([])              = ""
     * join([null])          = ""
     * join(["a", "b", "c"]) = "abc"
     * join([null, "", "a"]) = "a"
     * </pre>
     * 
     * @param array
     *            the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     * @since 2.0
     */
    public static String join(Object[] array) {
        return join(array, null);
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing
     * the provided list of elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. Null objects or empty
     * strings within the array are represented by empty strings.
     * </p>
     * 
     * <pre>
     * join(null, *)               = null
     * join([], *)                 = ""
     * join([null], *)             = ""
     * join(["a", "b", "c"], ';')  = "a;b;c"
     * join(["a", "b", "c"], null) = "abc"
     * join([null, "", "a"], ';')  = ";;a"
     * </pre>
     * 
     * @param array
     *            the array of values to join together, may be null
     * @param separator
     *            the separator character to use
     * @return the joined String, <code>null</code> if null array input
     * @since 2.0
     */
    public static String join(Object[] array, char separator) {
        if (array == null) {
            return null;
        }

        return join(array, separator, 0, array.length);
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing
     * the provided list of elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. Null objects or empty
     * strings within the array are represented by empty strings.
     * </p>
     * 
     * <pre>
     * join(null, *)               = null
     * join([], *)                 = ""
     * join([null], *)             = ""
     * join(["a", "b", "c"], ';')  = "a;b;c"
     * join(["a", "b", "c"], null) = "abc"
     * join([null, "", "a"], ';')  = ";;a"
     * </pre>
     * 
     * @param array
     *            the array of values to join together, may be null
     * @param separator
     *            the separator character to use
     * @param startIndex
     *            the first index to start joining from. It is an error to pass
     *            in an end index past the end of the array
     * @param endIndex
     *            the index to stop joining from (exclusive). It is an error to
     *            pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
     * @since 2.0
     */
    public static String join(Object[] array, char separator, int startIndex,
            int endIndex) {
        if (array == null) {
            return null;
        }
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY_STRING;
        }

        bufSize *= ((array[startIndex] == null ? 16 : array[startIndex]
                .toString().length()) + 1);
        StringBuilder buf = new StringBuilder(bufSize);

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
                buf.append(separator);
            }
            if (array[i] != null) {
                buf.append(array[i]);
            }
        }
        return buf.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing
     * the provided list of elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. A <code>null</code>
     * separator is the same as an empty String (""). Null objects or empty
     * strings within the array are represented by empty strings.
     * </p>
     * 
     * <pre>
     * join(null, *)                = null
     * join([], *)                  = ""
     * join([null], *)              = ""
     * join(["a", "b", "c"], "--")  = "a--b--c"
     * join(["a", "b", "c"], null)  = "abc"
     * join(["a", "b", "c"], "")    = "abc"
     * join([null, "", "a"], ',')   = ",,a"
     * </pre>
     * 
     * @param array
     *            the array of values to join together, may be null
     * @param separator
     *            the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(Object[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing
     * the provided list of elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. A <code>null</code>
     * separator is the same as an empty String (""). Null objects or empty
     * strings within the array are represented by empty strings.
     * </p>
     * 
     * <pre>
     * join(null, *)                = null
     * join([], *)                  = ""
     * join([null], *)              = ""
     * join(["a", "b", "c"], "--")  = "a--b--c"
     * join(["a", "b", "c"], null)  = "abc"
     * join(["a", "b", "c"], "")    = "abc"
     * join([null, "", "a"], ',')   = ",,a"
     * </pre>
     * 
     * @param array
     *            the array of values to join together, may be null
     * @param separator
     *            the separator character to use, null treated as ""
     * @param startIndex
     *            the first index to start joining from. It is an error to pass
     *            in an end index past the end of the array
     * @param endIndex
     *            the index to stop joining from (exclusive). It is an error to
     *            pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(Object[] array, String separator, int startIndex,
            int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY_STRING;
        }

        // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) +
        // len(separator))
        // (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY_STRING;
        }

        bufSize *= ((array[startIndex] == null ? 16 : array[startIndex]
                .toString().length()) + separator.length());

        StringBuilder buf = new StringBuilder(bufSize);

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
                buf.append(separator);
            }
            if (array[i] != null) {
                buf.append(array[i]);
            }
        }
        return buf.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided <code>Iterator</code> into a single
     * String containing the provided elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. Null objects or empty
     * strings within the iteration are represented by empty strings.
     * </p>
     * 
     * <p>
     * See the examples here: {@link #join(Object[],char)}.
     * </p>
     * 
     * @param iterator
     *            the <code>Iterator</code> of values to join together, may be
     *            null
     * @param separator
     *            the separator character to use
     * @return the joined String, <code>null</code> if null iterator input
     * @since 2.0
     */
    public static String join(Iterator<?> iterator, char separator) {

        // handle null, zero and one elements before building a buffer
        if (iterator == null) {
            return null;
        }
        if (!iterator.hasNext()) {
            return EMPTY_STRING;
        }
        Object first = iterator.next();
        if (!iterator.hasNext()) {
            return objectToString(first);
        }

        // two or more elements
        StringBuilder buf = new StringBuilder(256); // Java default is 16,
        // probably too small
        if (first != null) {
            buf.append(first);
        }

        while (iterator.hasNext()) {
            buf.append(separator);
            Object obj = iterator.next();
            if (obj != null) {
                buf.append(obj);
            }
        }

        return buf.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided <code>Iterator</code> into a single
     * String containing the provided elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. A <code>null</code>
     * separator is the same as an empty String ("").
     * </p>
     * 
     * <p>
     * See the examples here: {@link #join(Object[],String)}.
     * </p>
     * 
     * @param iterator
     *            the <code>Iterator</code> of values to join together, may be
     *            null
     * @param separator
     *            the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null iterator input
     */
    public static String join(Iterator<?> iterator, String separator) {

        // handle null, zero and one elements before building a buffer
        if (iterator == null) {
            return null;
        }
        if (!iterator.hasNext()) {
            return EMPTY_STRING;
        }
        Object first = iterator.next();
        if (!iterator.hasNext()) {
            return objectToString(first);
        }

        // two or more elements
        StringBuilder buf = new StringBuilder(256); // Java default is 16,
        // probably too small
        if (first != null) {
            buf.append(first);
        }

        while (iterator.hasNext()) {
            if (separator != null) {
                buf.append(separator);
            }
            Object obj = iterator.next();
            if (obj != null) {
                buf.append(obj);
            }
        }
        return buf.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided <code>Iterable</code> into a single
     * String containing the provided elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. Null objects or empty
     * strings within the iteration are represented by empty strings.
     * </p>
     * 
     * <p>
     * See the examples here: {@link #join(Object[],char)}.
     * </p>
     * 
     * @param iterable
     *            the <code>Iterable</code> providing the values to join
     *            together, may be null
     * @param separator
     *            the separator character to use
     * @return the joined String, <code>null</code> if null iterator input
     * @since 2.3
     */
    public static String join(Iterable<?> iterable, char separator) {
        if (iterable == null) {
            return null;
        }
        return join(iterable.iterator(), separator);
    }

    /**
     * <p>
     * Joins the elements of the provided <code>Iterable</code> into a single
     * String containing the provided elements.
     * </p>
     * 
     * <p>
     * No delimiter is added before or after the list. A <code>null</code>
     * separator is the same as an empty String ("").
     * </p>
     * 
     * <p>
     * See the examples here: {@link #join(Object[],String)}.
     * </p>
     * 
     * @param iterable
     *            the <code>Iterable</code> providing the values to join
     *            together, may be null
     * @param separator
     *            the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null iterator input
     * @since 2.3
     */
    public static String join(Iterable<?> iterable, String separator) {
        if (iterable == null) {
            return null;
        }
        return join(iterable.iterator(), separator);
    }

    // Delete
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Deletes all whitespaces from a String as defined by
     * {@link Character#isWhitespace(char)}.
     * </p>
     * 
     * <pre>
     * deleteWhitespace(null)         = null
     * deleteWhitespace("")           = ""
     * deleteWhitespace("abc")        = "abc"
     * deleteWhitespace("   ab  c  ") = "abc"
     * </pre>
     * 
     * @param str
     *            the String to delete whitespace from, may be null
     * @return the String without whitespaces, <code>null</code> if null String
     *         input
     */
    public static String deleteWhitespace(String str) {
        if (isEmpty(str)) {
            return str;
        }
        int sz = str.length();
        char[] chs = new char[sz];
        int count = 0;
        for (int i = 0; i < sz; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                chs[count++] = str.charAt(i);
            }
        }
        if (count == sz) {
            return str;
        }
        return new String(chs, 0, count);
    }

    // Remove
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Removes a substring only if it is at the begining of a source string,
     * otherwise returns the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string. A <code>null</code>
     * search string will return the source string.
     * </p>
     * 
     * <pre>
     * removeStart(null, *)      = null
     * removeStart("", *)        = ""
     * removeStart(*, null)      = *
     * removeStart("www.domain.com", "www.")   = "domain.com"
     * removeStart("domain.com", "www.")       = "domain.com"
     * removeStart("www.domain.com", "domain") = "www.domain.com"
     * removeStart("abc", "")    = "abc"
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the String to search for and remove, may be null
     * @return the substring with the string removed if found, <code>null</code>
     *         if null String input
     * @since 2.1
     */
    public static String removeStart(String str, String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        if (str.startsWith(remove)) {
            return str.substring(remove.length());
        }
        return str;
    }

    /**
     * <p>
     * Case insensitive removal of a substring if it is at the begining of a
     * source string, otherwise returns the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string. A <code>null</code>
     * search string will return the source string.
     * </p>
     * 
     * <pre>
     * removeStartIgnoreCase(null, *)      = null
     * removeStartIgnoreCase("", *)        = ""
     * removeStartIgnoreCase(*, null)      = *
     * removeStartIgnoreCase("www.domain.com", "www.")   = "domain.com"
     * removeStartIgnoreCase("www.domain.com", "WWW.")   = "domain.com"
     * removeStartIgnoreCase("domain.com", "www.")       = "domain.com"
     * removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
     * removeStartIgnoreCase("abc", "")    = "abc"
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the String to search for (case insensitive) and remove, may be
     *            null
     * @return the substring with the string removed if found, <code>null</code>
     *         if null String input
     * @since 2.4
     */
    public static String removeStartIgnoreCase(String str, String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        if (startsWithIgnoreCase(str, remove)) {
            return str.substring(remove.length());
        }
        return str;
    }

    /**
     * <p>
     * Removes a substring only if it is at the end of a source string,
     * otherwise returns the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string. A <code>null</code>
     * search string will return the source string.
     * </p>
     * 
     * <pre>
     * removeEnd(null, *)      = null
     * removeEnd("", *)        = ""
     * removeEnd(*, null)      = *
     * removeEnd("www.domain.com", ".com.")  = "www.domain.com"
     * removeEnd("www.domain.com", ".com")   = "www.domain"
     * removeEnd("www.domain.com", "domain") = "www.domain.com"
     * removeEnd("abc", "")    = "abc"
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the String to search for and remove, may be null
     * @return the substring with the string removed if found, <code>null</code>
     *         if null String input
     * @since 2.1
     */
    public static String removeEnd(String str, String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        if (str.endsWith(remove)) {
            return str.substring(0, str.length() - remove.length());
        }
        return str;
    }

    /**
     * <p>
     * Case insensitive removal of a substring if it is at the end of a source
     * string, otherwise returns the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string. A <code>null</code>
     * search string will return the source string.
     * </p>
     * 
     * <pre>
     * removeEndIgnoreCase(null, *)      = null
     * removeEndIgnoreCase("", *)        = ""
     * removeEndIgnoreCase(*, null)      = *
     * removeEndIgnoreCase("www.domain.com", ".com.")  = "www.domain.com"
     * removeEndIgnoreCase("www.domain.com", ".com")   = "www.domain"
     * removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
     * removeEndIgnoreCase("abc", "")    = "abc"
     * removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
     * removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the String to search for (case insensitive) and remove, may be
     *            null
     * @return the substring with the string removed if found, <code>null</code>
     *         if null String input
     * @since 2.4
     */
    public static String removeEndIgnoreCase(String str, String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        if (endsWithIgnoreCase(str, remove)) {
            return str.substring(0, str.length() - remove.length());
        }
        return str;
    }

    /**
     * <p>
     * Removes all occurrences of a substring from within the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string. A <code>null</code>
     * remove string will return the source string. An empty ("") remove string
     * will return the source string.
     * </p>
     * 
     * <pre>
     * remove(null, *)        = null
     * remove("", *)          = ""
     * remove(*, null)        = *
     * remove(*, "")          = *
     * remove("queued", "ue") = "qd"
     * remove("queued", "zz") = "queued"
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the String to search for and remove, may be null
     * @return the substring with the string removed if found, <code>null</code>
     *         if null String input
     * @since 2.1
     */
    public static String remove(String str, String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        return replace(str, remove, EMPTY_STRING, -1);
    }

    /**
     * <p>
     * Removes all occurrences of a character from within the source string.
     * </p>
     * 
     * <p>
     * A <code>null</code> source string will return <code>null</code>. An empty
     * ("") source string will return the empty string.
     * </p>
     * 
     * <pre>
     * remove(null, *)       = null
     * remove("", *)         = ""
     * remove("queued", 'u') = "qeed"
     * remove("queued", 'z') = "queued"
     * </pre>
     * 
     * @param str
     *            the source String to search, may be null
     * @param remove
     *            the char to search for and remove, may be null
     * @return the substring with the char removed if found, <code>null</code>
     *         if null String input
     * @since 2.1
     */
    public static String remove(String str, char remove) {
        if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) {
            return str;
        }
        char[] chars = str.toCharArray();
        int pos = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] != remove) {
                chars[pos++] = chars[i];
            }
        }
        return new String(chars, 0, pos);
    }

    // Replacing
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Replaces a String with another String inside a larger String, once.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op.
     * </p>
     * 
     * <pre>
     * replaceOnce(null, *, *)        = null
     * replaceOnce("", *, *)          = ""
     * replaceOnce("any", null, *)    = "any"
     * replaceOnce("any", *, null)    = "any"
     * replaceOnce("any", "", *)      = "any"
     * replaceOnce("aba", "a", null)  = "aba"
     * replaceOnce("aba", "a", "")    = "ba"
     * replaceOnce("aba", "a", "z")   = "zba"
     * </pre>
     * 
     * @see #replace(String text, String searchString, String replacement, int
     *      max)
     * @param text
     *            text to search and replace in, may be null
     * @param searchString
     *            the String to search for, may be null
     * @param replacement
     *            the String to replace with, may be null
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     */
    public static String replaceOnce(String text, String searchString,
            String replacement) {
        return replace(text, searchString, replacement, 1);
    }

    /**
     * <p>
     * Replaces all occurrences of a String within another String.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op.
     * </p>
     * 
     * <pre>
     * replace(null, *, *)        = null
     * replace("", *, *)          = ""
     * replace("any", null, *)    = "any"
     * replace("any", *, null)    = "any"
     * replace("any", "", *)      = "any"
     * replace("aba", "a", null)  = "aba"
     * replace("aba", "a", "")    = "b"
     * replace("aba", "a", "z")   = "zbz"
     * </pre>
     * 
     * @see #replace(String text, String searchString, String replacement, int
     *      max)
     * @param text
     *            text to search and replace in, may be null
     * @param searchString
     *            the String to search for, may be null
     * @param replacement
     *            the String to replace it with, may be null
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     */
    public static String replace(String text, String searchString,
            String replacement) {
        return replace(text, searchString, replacement, -1);
    }

    /**
     * <p>
     * Replaces a String with another String inside a larger String, for the
     * first <code>max</code> values of the search String.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op.
     * </p>
     * 
     * <pre>
     * replace(null, *, *, *)         = null
     * replace("", *, *, *)           = ""
     * replace("any", null, *, *)     = "any"
     * replace("any", *, null, *)     = "any"
     * replace("any", "", *, *)       = "any"
     * replace("any", *, *, 0)        = "any"
     * replace("abaa", "a", null, -1) = "abaa"
     * replace("abaa", "a", "", -1)   = "b"
     * replace("abaa", "a", "z", 0)   = "abaa"
     * replace("abaa", "a", "z", 1)   = "zbaa"
     * replace("abaa", "a", "z", 2)   = "zbza"
     * replace("abaa", "a", "z", -1)  = "zbzz"
     * </pre>
     * 
     * @param text
     *            text to search and replace in, may be null
     * @param searchString
     *            the String to search for, may be null
     * @param replacement
     *            the String to replace it with, may be null
     * @param max
     *            maximum number of values to replace, or <code>-1</code> if no
     *            maximum
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     */
    public static String replace(String text, String searchString,
            String replacement, int max) {
        if (isEmpty(text) || isEmpty(searchString) || replacement == null
                || max == 0) {
            return text;
        }
        int start = 0;
        int end = text.indexOf(searchString, start);
        if (end == INDEX_NOT_FOUND) {
            return text;
        }
        int replLength = searchString.length();
        int increase = replacement.length() - replLength;
        increase = (increase < 0 ? 0 : increase);
        increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
        StringBuilder buf = new StringBuilder(text.length() + increase);
        while (end != INDEX_NOT_FOUND) {
            buf.append(text.substring(start, end)).append(replacement);
            start = end + replLength;
            if (--max == 0) {
                break;
            }
            end = text.indexOf(searchString, start);
        }
        buf.append(text.substring(start));
        return buf.toString();
    }

    /**
     * <p>
     * Replaces all occurrences of Strings within another String.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op, or if any
     * "search string" or "string to replace" is null, that replace will be
     * ignored. This will not repeat. For repeating replaces, call the
     * overloaded method.
     * </p>
     * 
     * <pre>
     *  replaceEach(null, *, *)        = null
     *  replaceEach("", *, *)          = ""
     *  replaceEach("aba", null, null) = "aba"
     *  replaceEach("aba", new String[0], null) = "aba"
     *  replaceEach("aba", null, new String[0]) = "aba"
     *  replaceEach("aba", new String[]{"a"}, null)  = "aba"
     *  replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
     *  replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
     *  (example of how it does not repeat)
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
     * </pre>
     * 
     * @param text
     *            text to search and replace in, no-op if null
     * @param searchList
     *            the Strings to search for, no-op if null
     * @param replacementList
     *            the Strings to replace them with, no-op if null
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     * @throws IndexOutOfBoundsException
     *             if the lengths of the arrays are not the same (null is ok,
     *             and/or size 0)
     * @since 2.4
     */
    public static String replaceEach(String text, String[] searchList,
            String[] replacementList) {
        return replaceEach(text, searchList, replacementList, false, 0);
    }

    /**
     * <p>
     * Replaces all occurrences of Strings within another String.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op, or if any
     * "search string" or "string to replace" is null, that replace will be
     * ignored. This will not repeat. For repeating replaces, call the
     * overloaded method.
     * </p>
     * 
     * <pre>
     *  replaceEach(null, *, *, *) = null
     *  replaceEach("", *, *, *) = ""
     *  replaceEach("aba", null, null, *) = "aba"
     *  replaceEach("aba", new String[0], null, *) = "aba"
     *  replaceEach("aba", null, new String[0], *) = "aba"
     *  replaceEach("aba", new String[]{"a"}, null, *) = "aba"
     *  replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
     *  replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
     *  (example of how it repeats)
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalArgumentException
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
     * </pre>
     * 
     * @param text
     *            text to search and replace in, no-op if null
     * @param searchList
     *            the Strings to search for, no-op if null
     * @param replacementList
     *            the Strings to replace them with, no-op if null
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     * @throws IllegalArgumentException
     *             if the search is repeating and there is an endless loop due
     *             to outputs of one being inputs to another
     * @throws IndexOutOfBoundsException
     *             if the lengths of the arrays are not the same (null is ok,
     *             and/or size 0)
     * @since 2.4
     */
    public static String replaceEachRepeatedly(String text,
            String[] searchList, String[] replacementList) {
        // timeToLive should be 0 if not used or nothing to replace, else it's
        // the length of the replace array
        int timeToLive = searchList == null ? 0 : searchList.length;
        return replaceEach(text, searchList, replacementList, true, timeToLive);
    }

    /**
     * <p>
     * Replaces all occurrences of Strings within another String.
     * </p>
     * 
     * <p>
     * A <code>null</code> reference passed to this method is a no-op, or if any
     * "search string" or "string to replace" is null, that replace will be
     * ignored.
     * </p>
     * 
     * <pre>
     *  replaceEach(null, *, *, *) = null
     *  replaceEach("", *, *, *) = ""
     *  replaceEach("aba", null, null, *) = "aba"
     *  replaceEach("aba", new String[0], null, *) = "aba"
     *  replaceEach("aba", null, new String[0], *) = "aba"
     *  replaceEach("aba", new String[]{"a"}, null, *) = "aba"
     *  replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
     *  replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
     *  (example of how it repeats)
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
     *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalArgumentException
     * </pre>
     * 
     * @param text
     *            text to search and replace in, no-op if null
     * @param searchList
     *            the Strings to search for, no-op if null
     * @param replacementList
     *            the Strings to replace them with, no-op if null
     * @param repeat
     *            if true, then replace repeatedly until there are no more
     *            possible replacements or timeToLive < 0
     * @param timeToLive
     *            if less than 0 then there is a circular reference and endless
     *            loop
     * @return the text with any replacements processed, <code>null</code> if
     *         null String input
     * @throws IllegalArgumentException
     *             if the search is repeating and there is an endless loop due
     *             to outputs of one being inputs to another
     * @throws IndexOutOfBoundsException
     *             if the lengths of the arrays are not the same (null is ok,
     *             and/or size 0)
     * @since 2.4
     */
    private static String replaceEach(String text, String[] searchList,
            String[] replacementList, boolean repeat, int timeToLive) {

        // mchyzer Performance note: This creates very few new objects (one
        // major goal)
        // let me know if there are performance requests, we can create a
        // harness to measure

        if (text == null || text.length() == 0 || searchList == null
                || searchList.length == 0 || replacementList == null
                || replacementList.length == 0) {
            return text;
        }

        // if recursing, this shouldnt be less than 0
        if (timeToLive < 0) {
            throw new IllegalStateException("TimeToLive of " + timeToLive
                    + " is less than 0: " + text);
        }

        int searchLength = searchList.length;
        int replacementLength = replacementList.length;

        // make sure lengths are ok, these need to be equal
        if (searchLength != replacementLength) {
            throw new IllegalArgumentException(
                    "Search and Replace array lengths don't match: "
                            + searchLength + " vs " + replacementLength);
        }

        // keep track of which still have matches
        boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];

        // index on index that the match was found
        int textIndex = -1;
        int replaceIndex = -1;
        int tempIndex = -1;

        // index of replace array that will replace the search string found
        // NOTE: logic duplicated below START
        for (int i = 0; i < searchLength; i++) {
            if (noMoreMatchesForReplIndex[i] || searchList[i] == null
                    || searchList[i].length() == 0
                    || replacementList[i] == null) {
                continue;
            }
            tempIndex = text.indexOf(searchList[i]);

            // see if we need to keep searching for this
            if (tempIndex == -1) {
                noMoreMatchesForReplIndex[i] = true;
            } else {
                if (textIndex == -1 || tempIndex < textIndex) {
                    textIndex = tempIndex;
                    replaceIndex = i;
                }
            }
        }
        // NOTE: logic mostly below END

        // no search strings found, we are done
        if (textIndex == -1) {
            return text;
        }

        int start = 0;

        // get a good guess on the size of the result buffer so it doesnt have
        // to double if it goes over a bit
        int increase = 0;

        // count the replacement text elements that are larger than their
        // corresponding text being replaced
        for (int i = 0; i < searchList.length; i++) {
            if (searchList[i] == null || replacementList[i] == null) {
                continue;
            }
            int greater = replacementList[i].length() - searchList[i].length();
            if (greater > 0) {
                increase += 3 * greater; // assume 3 matches
            }
        }
        // have upper-bound at 20% increase, then let Java take over
        increase = Math.min(increase, text.length() / 5);

        StringBuilder buf = new StringBuilder(text.length() + increase);

        while (textIndex != -1) {

            for (int i = start; i < textIndex; i++) {
                buf.append(text.charAt(i));
            }
            buf.append(replacementList[replaceIndex]);

            start = textIndex + searchList[replaceIndex].length();

            textIndex = -1;
            replaceIndex = -1;
            tempIndex = -1;
            // find the next earliest match
            // NOTE: logic mostly duplicated above START
            for (int i = 0; i < searchLength; i++) {
                if (noMoreMatchesForReplIndex[i] || searchList[i] == null
                        || searchList[i].length() == 0
                        || replacementList[i] == null) {
                    continue;
                }
                tempIndex = text.indexOf(searchList[i], start);

                // see if we need to keep searching for this
                if (tempIndex == -1) {
                    noMoreMatchesForReplIndex[i] = true;
                } else {
                    if (textIndex == -1 || tempIndex < textIndex) {
                        textIndex = tempIndex;
                        replaceIndex = i;
                    }
                }
            }
            // NOTE: logic duplicated above END

        }
        int textLength = text.length();
        for (int i = start; i < textLength; i++) {
            buf.append(text.charAt(i));
        }
        String result = buf.toString();
        if (!repeat) {
            return result;
        }

        return replaceEach(result, searchList, replacementList, repeat,
                timeToLive - 1);
    }

    // Replace, character based
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Replaces all occurrences of a character in a String with another. This is
     * a null-safe version of {@link String#replace(char, char)}.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input returns <code>null</code>. An empty ("")
     * string input returns an empty string.
     * </p>
     * 
     * <pre>
     * replaceChars(null, *, *)        = null
     * replaceChars("", *, *)          = ""
     * replaceChars("abcba", 'b', 'y') = "aycya"
     * replaceChars("abcba", 'z', 'y') = "abcba"
     * </pre>
     * 
     * @param str
     *            String to replace characters in, may be null
     * @param searchChar
     *            the character to search for, may be null
     * @param replaceChar
     *            the character to replace, may be null
     * @return modified String, <code>null</code> if null string input
     * @since 2.0
     */
    public static String replaceChars(String str, char searchChar,
            char replaceChar) {
        if (str == null) {
            return null;
        }
        return str.replace(searchChar, replaceChar);
    }

    /**
     * <p>
     * Replaces multiple characters in a String in one go. This method can also
     * be used to delete characters.
     * </p>
     * 
     * <p>
     * For example:<br />
     * <code>replaceChars(&quot;hello&quot;, &quot;ho&quot;, &quot;jy&quot;) = jelly</code>
     * .
     * </p>
     * 
     * <p>
     * A <code>null</code> string input returns <code>null</code>. An empty ("")
     * string input returns an empty string. A null or empty set of search
     * characters returns the input string.
     * </p>
     * 
     * <p>
     * The length of the search characters should normally equal the length of
     * the replace characters. If the search characters is longer, then the
     * extra search characters are deleted. If the search characters is shorter,
     * then the extra replace characters are ignored.
     * </p>
     * 
     * <pre>
     * replaceChars(null, *, *)           = null
     * replaceChars("", *, *)             = ""
     * replaceChars("abc", null, *)       = "abc"
     * replaceChars("abc", "", *)         = "abc"
     * replaceChars("abc", "b", null)     = "ac"
     * replaceChars("abc", "b", "")       = "ac"
     * replaceChars("abcba", "bc", "yz")  = "ayzya"
     * replaceChars("abcba", "bc", "y")   = "ayya"
     * replaceChars("abcba", "bc", "yzx") = "ayzya"
     * </pre>
     * 
     * @param str
     *            String to replace characters in, may be null
     * @param searchChars
     *            a set of characters to search for, may be null
     * @param replaceChars
     *            a set of characters to replace, may be null
     * @return modified String, <code>null</code> if null string input
     * @since 2.0
     */
    public static String replaceChars(String str, String searchChars,
            String replaceChars) {
        if (isEmpty(str) || isEmpty(searchChars)) {
            return str;
        }
        if (replaceChars == null) {
            replaceChars = EMPTY_STRING;
        }
        boolean modified = false;
        int replaceCharsLength = replaceChars.length();
        int strLength = str.length();
        StringBuilder buf = new StringBuilder(strLength);
        for (int i = 0; i < strLength; i++) {
            char ch = str.charAt(i);
            int index = searchChars.indexOf(ch);
            if (index >= 0) {
                modified = true;
                if (index < replaceCharsLength) {
                    buf.append(replaceChars.charAt(index));
                }
            } else {
                buf.append(ch);
            }
        }
        if (modified) {
            return buf.toString();
        }
        return str;
    }

    // Overlay
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Overlays part of a String with another String.
     * </p>
     * 
     * <p>
     * A <code>null</code> string input returns <code>null</code>. A negative
     * index is treated as zero. An index greater than the string length is
     * treated as the string length. The start index is always the smaller of
     * the two indices.
     * </p>
     * 
     * <pre>
     * overlay(null, *, *, *)            = null
     * overlay("", "abc", 0, 0)          = "abc"
     * overlay("abcdef", null, 2, 4)     = "abef"
     * overlay("abcdef", "", 2, 4)       = "abef"
     * overlay("abcdef", "", 4, 2)       = "abef"
     * overlay("abcdef", "zzzz", 2, 4)   = "abzzzzef"
     * overlay("abcdef", "zzzz", 4, 2)   = "abzzzzef"
     * overlay("abcdef", "zzzz", -1, 4)  = "zzzzef"
     * overlay("abcdef", "zzzz", 2, 8)   = "abzzzz"
     * overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef"
     * overlay("abcdef", "zzzz", 8, 10)  = "abcdefzzzz"
     * </pre>
     * 
     * @param str
     *            the String to do overlaying in, may be null
     * @param overlay
     *            the String to overlay, may be null
     * @param start
     *            the position to start overlaying at
     * @param end
     *            the position to stop overlaying before
     * @return overlayed String, <code>null</code> if null String input
     * @since 2.0
     */
    public static String overlay(String str, String overlay, int start, int end) {
        if (str == null) {
            return null;
        }
        if (overlay == null) {
            overlay = EMPTY_STRING;
        }
        int len = str.length();
        if (start < 0) {
            start = 0;
        }
        if (start > len) {
            start = len;
        }
        if (end < 0) {
            end = 0;
        }
        if (end > len) {
            end = len;
        }
        if (start > end) {
            int temp = start;
            start = end;
            end = temp;
        }
        return new StringBuilder(len + start - end + overlay.length() + 1)
                .append(str.substring(0, start)).append(overlay)
                .append(str.substring(end)).toString();
    }

    // Chomping
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Removes one newline from end of a String if it's there, otherwise leave
     * it alone. A newline is &quot;<code>\n</code>&quot;, &quot;<code>\r</code>
     * &quot;, or &quot;<code>\r\n</code>&quot;.
     * </p>
     * 
     * <p>
     * NOTE: This method changed in 2.0. It now more closely matches Perl chomp.
     * </p>
     * 
     * <pre>
     * chomp(null)          = null
     * chomp("")            = ""
     * chomp("abc \r")      = "abc "
     * chomp("abc\n")       = "abc"
     * chomp("abc\r\n")     = "abc"
     * chomp("abc\r\n\r\n") = "abc\r\n"
     * chomp("abc\n\r")     = "abc\n"
     * chomp("abc\n\rabc")  = "abc\n\rabc"
     * chomp("\r")          = ""
     * chomp("\n")          = ""
     * chomp("\r\n")        = ""
     * </pre>
     * 
     * @param str
     *            the String to chomp a newline from, may be null
     * @return String without newline, <code>null</code> if null String input
     */
    public static String chomp(String str) {
        if (isEmpty(str)) {
            return str;
        }

        if (str.length() == 1) {
            char ch = str.charAt(0);
            if (ch == CHAR_CR || ch == CHAR_LF) {
                return EMPTY_STRING;
            }
            return str;
        }

        int lastIdx = str.length() - 1;
        char last = str.charAt(lastIdx);

        if (last == CHAR_LF) {
            if (str.charAt(lastIdx - 1) == CHAR_CR) {
                lastIdx--;
            }
        } else if (last != CHAR_CR) {
            lastIdx++;
        }
        return str.substring(0, lastIdx);
    }

    /**
     * <p>
     * Removes <code>separator</code> from the end of <code>str</code> if it's
     * there, otherwise leave it alone.
     * </p>
     * 
     * <p>
     * NOTE: This method changed in version 2.0. It now more closely matches
     * Perl chomp. For the previous behavior, use
     * {@link #substringBeforeLast(String, String)}. This method uses
     * {@link String#endsWith(String)}.
     * </p>
     * 
     * <pre>
     * chomp(null, *)         = null
     * chomp("", *)           = ""
     * chomp("foobar", "bar") = "foo"
     * chomp("foobar", "baz") = "foobar"
     * chomp("foo", "foo")    = ""
     * chomp("foo ", "foo")   = "foo "
     * chomp(" foo", "foo")   = " "
     * chomp("foo", "foooo")  = "foo"
     * chomp("foo", "")       = "foo"
     * chomp("foo", null)     = "foo"
     * </pre>
     * 
     * @param str
     *            the String to chomp from, may be null
     * @param separator
     *            separator String, may be null
     * @return String without trailing separator, <code>null</code> if null
     *         String input
     */
    public static String chomp(String str, String separator) {
        if (isEmpty(str) || separator == null) {
            return str;
        }
        if (str.endsWith(separator)) {
            return str.substring(0, str.length() - separator.length());
        }
        return str;
    }

    // Chopping
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Remove the last character from a String.
     * </p>
     * 
     * <p>
     * If the String ends in <code>\r\n</code>, then remove both of them.
     * </p>
     * 
     * <pre>
     * chop(null)          = null
     * chop("")            = ""
     * chop("abc \r")      = "abc "
     * chop("abc\n")       = "abc"
     * chop("abc\r\n")     = "abc"
     * chop("abc")         = "ab"
     * chop("abc\nabc")    = "abc\nab"
     * chop("a")           = ""
     * chop("\r")          = ""
     * chop("\n")          = ""
     * chop("\r\n")        = ""
     * </pre>
     * 
     * @param str
     *            the String to chop last character from, may be null
     * @return String without last character, <code>null</code> if null String
     *         input
     */
    public static String chop(String str) {
        if (str == null) {
            return null;
        }
        int strLen = str.length();
        if (strLen < 2) {
            return EMPTY_STRING;
        }
        int lastIdx = strLen - 1;
        String ret = str.substring(0, lastIdx);
        char last = str.charAt(lastIdx);
        if (last == CHAR_LF) {
            if (ret.charAt(lastIdx - 1) == CHAR_CR) {
                return ret.substring(0, lastIdx - 1);
            }
        }
        return ret;
    }

    // Conversion
    // -----------------------------------------------------------------------

    // Padding
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Repeat a String <code>repeat</code> times to form a new String.
     * </p>
     * 
     * <pre>
     * repeat(null, 2) = null
     * repeat("", 0)   = ""
     * repeat("", 2)   = ""
     * repeat("a", 3)  = "aaa"
     * repeat("ab", 2) = "abab"
     * repeat("a", -2) = ""
     * </pre>
     * 
     * @param str
     *            the String to repeat, may be null
     * @param repeat
     *            number of times to repeat str, negative treated as zero
     * @return a new String consisting of the original String repeated,
     *         <code>null</code> if null String input
     */
    public static String repeat(String str, int repeat) {
        // Performance tuned for 2.0 (JDK1.4)

        if (str == null) {
            return null;
        }
        if (repeat <= 0) {
            return EMPTY_STRING;
        }
        int inputLength = str.length();
        if (repeat == 1 || inputLength == 0) {
            return str;
        }
        if (inputLength == 1 && repeat <= PAD_LIMIT) {
            return padding(repeat, str.charAt(0));
        }

        int outputLength = inputLength * repeat;
        switch (inputLength) {
            case 1:
                char ch = str.charAt(0);
                char[] output1 = new char[outputLength];
                for (int i = repeat - 1; i >= 0; i--) {
                    output1[i] = ch;
                }
                return new String(output1);
            case 2:
                char ch0 = str.charAt(0);
                char ch1 = str.charAt(1);
                char[] output2 = new char[outputLength];
                for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
                    output2[i] = ch0;
                    output2[i + 1] = ch1;
                }
                return new String(output2);
            default:
                StringBuilder buf = new StringBuilder(outputLength);
                for (int i = 0; i < repeat; i++) {
                    buf.append(str);
                }
                return buf.toString();
        }
    }

    /**
     * <p>
     * Repeat a String <code>repeat</code> times to form a new String, with a
     * String separator injected each time.
     * </p>
     * 
     * <pre>
     * repeat(null, null, 2) = null
     * repeat(null, "x", 2)  = null
     * repeat("", null, 0)   = ""
     * repeat("", "", 2)     = ""
     * repeat("", "x", 3)    = "xxx"
     * repeat("?", ", ", 3)  = "?, ?, ?"
     * </pre>
     * 
     * @param str
     *            the String to repeat, may be null
     * @param separator
     *            the String to inject, may be null
     * @param repeat
     *            number of times to repeat str, negative treated as zero
     * @return a new String consisting of the original String repeated,
     *         <code>null</code> if null String input
     * @since 2.5
     */
    public static String repeat(String str, String separator, int repeat) {
        if (str == null || separator == null) {
            return repeat(str, repeat);
        } else {
            // given that repeat(String, int) is quite optimized, better to rely
            // on it than try and splice this into it
            String result = repeat(str + separator, repeat);
            return removeEnd(result, separator);
        }
    }

    /**
     * <p>
     * Returns padding using the specified delimiter repeated to a given length.
     * </p>
     * 
     * <pre>
     * padding(0, 'e')  = ""
     * padding(3, 'e')  = "eee"
     * padding(-2, 'e') = IndexOutOfBoundsException
     * </pre>
     * 
     * <p>
     * Note: this method doesn't not support padding with <a
     * href="http://www.unicode.org/glossary/#supplementary_character">Unicode
     * Supplementary Characters</a> as they require a pair of <code>char</code>s
     * to be represented. If you are needing to support full I18N of your
     * applications consider using {@link #repeat(String, int)} instead.
     * </p>
     * 
     * @param repeat
     *            number of times to repeat delim
     * @param padChar
     *            character to repeat
     * @return String with repeated character
     * @throws IndexOutOfBoundsException
     *             if <code>repeat &lt; 0</code>
     * @see #repeat(String, int)
     */
    private static String padding(int repeat, char padChar)
            throws IndexOutOfBoundsException {
        if (repeat < 0) {
            throw new IndexOutOfBoundsException(
                    "Cannot pad a negative amount: " + repeat);
        }
        final char[] buf = new char[repeat];
        for (int i = 0; i < buf.length; i++) {
            buf[i] = padChar;
        }
        return new String(buf);
    }

    /**
     * <p>
     * Right pad a String with spaces (' ').
     * </p>
     * 
     * <p>
     * The String is padded to the size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * rightPad(null, *)   = null
     * rightPad("", 3)     = "   "
     * rightPad("bat", 3)  = "bat"
     * rightPad("bat", 5)  = "bat  "
     * rightPad("bat", 1)  = "bat"
     * rightPad("bat", -1) = "bat"
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @return right padded String or original String if no padding is
     *         necessary, <code>null</code> if null String input
     */
    public static String rightPad(String str, int size) {
        return rightPad(str, size, ' ');
    }

    /**
     * <p>
     * Right pad a String with a specified character.
     * </p>
     * 
     * <p>
     * The String is padded to the size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * rightPad(null, *, *)     = null
     * rightPad("", 3, 'z')     = "zzz"
     * rightPad("bat", 3, 'z')  = "bat"
     * rightPad("bat", 5, 'z')  = "batzz"
     * rightPad("bat", 1, 'z')  = "bat"
     * rightPad("bat", -1, 'z') = "bat"
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @param padChar
     *            the character to pad with
     * @return right padded String or original String if no padding is
     *         necessary, <code>null</code> if null String input
     * @since 2.0
     */
    public static String rightPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        }
        int pads = size - str.length();
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        if (pads > PAD_LIMIT) {
            return rightPad(str, size, String.valueOf(padChar));
        }
        return str.concat(padding(pads, padChar));
    }

    /**
     * <p>
     * Right pad a String with a specified String.
     * </p>
     * 
     * <p>
     * The String is padded to the size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * rightPad(null, *, *)      = null
     * rightPad("", 3, "z")      = "zzz"
     * rightPad("bat", 3, "yz")  = "bat"
     * rightPad("bat", 5, "yz")  = "batyz"
     * rightPad("bat", 8, "yz")  = "batyzyzy"
     * rightPad("bat", 1, "yz")  = "bat"
     * rightPad("bat", -1, "yz") = "bat"
     * rightPad("bat", 5, null)  = "bat  "
     * rightPad("bat", 5, "")    = "bat  "
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @param padStr
     *            the String to pad with, null or empty treated as single space
     * @return right padded String or original String if no padding is
     *         necessary, <code>null</code> if null String input
     */
    public static String rightPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        }
        if (isEmpty(padStr)) {
            padStr = " ";
        }
        int padLen = padStr.length();
        int strLen = str.length();
        int pads = size - strLen;
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        if (padLen == 1 && pads <= PAD_LIMIT) {
            return rightPad(str, size, padStr.charAt(0));
        }

        if (pads == padLen) {
            return str.concat(padStr);
        } else if (pads < padLen) {
            return str.concat(padStr.substring(0, pads));
        } else {
            char[] padding = new char[pads];
            char[] padChars = padStr.toCharArray();
            for (int i = 0; i < pads; i++) {
                padding[i] = padChars[i % padLen];
            }
            return str.concat(new String(padding));
        }
    }

    /**
     * <p>
     * Left pad a String with spaces (' ').
     * </p>
     * 
     * <p>
     * The String is padded to the size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * leftPad(null, *)   = null
     * leftPad("", 3)     = "   "
     * leftPad("bat", 3)  = "bat"
     * leftPad("bat", 5)  = "  bat"
     * leftPad("bat", 1)  = "bat"
     * leftPad("bat", -1) = "bat"
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @return left padded String or original String if no padding is necessary,
     *         <code>null</code> if null String input
     */
    public static String leftPad(String str, int size) {
        return leftPad(str, size, ' ');
    }

    /**
     * <p>
     * Left pad a String with a specified character.
     * </p>
     * 
     * <p>
     * Pad to a size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * leftPad(null, *, *)     = null
     * leftPad("", 3, 'z')     = "zzz"
     * leftPad("bat", 3, 'z')  = "bat"
     * leftPad("bat", 5, 'z')  = "zzbat"
     * leftPad("bat", 1, 'z')  = "bat"
     * leftPad("bat", -1, 'z') = "bat"
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @param padChar
     *            the character to pad with
     * @return left padded String or original String if no padding is necessary,
     *         <code>null</code> if null String input
     * @since 2.0
     */
    public static String leftPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        }
        int pads = size - str.length();
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        if (pads > PAD_LIMIT) {
            return leftPad(str, size, String.valueOf(padChar));
        }
        return padding(pads, padChar).concat(str);
    }

    /**
     * <p>
     * Left pad a String with a specified String.
     * </p>
     * 
     * <p>
     * Pad to a size of <code>size</code>.
     * </p>
     * 
     * <pre>
     * leftPad(null, *, *)      = null
     * leftPad("", 3, "z")      = "zzz"
     * leftPad("bat", 3, "yz")  = "bat"
     * leftPad("bat", 5, "yz")  = "yzbat"
     * leftPad("bat", 8, "yz")  = "yzyzybat"
     * leftPad("bat", 1, "yz")  = "bat"
     * leftPad("bat", -1, "yz") = "bat"
     * leftPad("bat", 5, null)  = "  bat"
     * leftPad("bat", 5, "")    = "  bat"
     * </pre>
     * 
     * @param str
     *            the String to pad out, may be null
     * @param size
     *            the size to pad to
     * @param padStr
     *            the String to pad with, null or empty treated as single space
     * @return left padded String or original String if no padding is necessary,
     *         <code>null</code> if null String input
     */
    public static String leftPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        }
        if (isEmpty(padStr)) {
            padStr = " ";
        }
        int padLen = padStr.length();
        int strLen = str.length();
        int pads = size - strLen;
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        if (padLen == 1 && pads <= PAD_LIMIT) {
            return leftPad(str, size, padStr.charAt(0));
        }

        if (pads == padLen) {
            return padStr.concat(str);
        } else if (pads < padLen) {
            return padStr.substring(0, pads).concat(str);
        } else {
            char[] padding = new char[pads];
            char[] padChars = padStr.toCharArray();
            for (int i = 0; i < pads; i++) {
                padding[i] = padChars[i % padLen];
            }
            return new String(padding).concat(str);
        }
    }

    // Centering
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Centers a String in a larger String of size <code>size</code> using the
     * space character (' ').
     * <p>
     * 
     * <p>
     * If the size is less than the String length, the String is returned. A
     * <code>null</code> String returns <code>null</code>. A negative size is
     * treated as zero.
     * </p>
     * 
     * <p>
     * Equivalent to <code>center(str, size, " ")</code>.
     * </p>
     * 
     * <pre>
     * center(null, *)   = null
     * center("", 4)     = "    "
     * center("ab", -1)  = "ab"
     * center("ab", 4)   = " ab "
     * center("abcd", 2) = "abcd"
     * center("a", 4)    = " a  "
     * </pre>
     * 
     * @param str
     *            the String to center, may be null
     * @param size
     *            the int size of new String, negative treated as zero
     * @return centered String, <code>null</code> if null String input
     */
    public static String center(String str, int size) {
        return center(str, size, ' ');
    }

    /**
     * <p>
     * Centers a String in a larger String of size <code>size</code>. Uses a
     * supplied character as the value to pad the String with.
     * </p>
     * 
     * <p>
     * If the size is less than the String length, the String is returned. A
     * <code>null</code> String returns <code>null</code>. A negative size is
     * treated as zero.
     * </p>
     * 
     * <pre>
     * center(null, *, *)     = null
     * center("", 4, ' ')     = "    "
     * center("ab", -1, ' ')  = "ab"
     * center("ab", 4, ' ')   = " ab"
     * center("abcd", 2, ' ') = "abcd"
     * center("a", 4, ' ')    = " a  "
     * center("a", 4, 'y')    = "yayy"
     * </pre>
     * 
     * @param str
     *            the String to center, may be null
     * @param size
     *            the int size of new String, negative treated as zero
     * @param padChar
     *            the character to pad the new String with
     * @return centered String, <code>null</code> if null String input
     * @since 2.0
     */
    public static String center(String str, int size, char padChar) {
        if (str == null || size <= 0) {
            return str;
        }
        int strLen = str.length();
        int pads = size - strLen;
        if (pads <= 0) {
            return str;
        }
        str = leftPad(str, strLen + pads / 2, padChar);
        str = rightPad(str, size, padChar);
        return str;
    }

    /**
     * <p>
     * Centers a String in a larger String of size <code>size</code>. Uses a
     * supplied String as the value to pad the String with.
     * </p>
     * 
     * <p>
     * If the size is less than the String length, the String is returned. A
     * <code>null</code> String returns <code>null</code>. A negative size is
     * treated as zero.
     * </p>
     * 
     * <pre>
     * center(null, *, *)     = null
     * center("", 4, " ")     = "    "
     * center("ab", -1, " ")  = "ab"
     * center("ab", 4, " ")   = " ab"
     * center("abcd", 2, " ") = "abcd"
     * center("a", 4, " ")    = " a  "
     * center("a", 4, "yz")   = "yayz"
     * center("abc", 7, null) = "  abc  "
     * center("abc", 7, "")   = "  abc  "
     * </pre>
     * 
     * @param str
     *            the String to center, may be null
     * @param size
     *            the int size of new String, negative treated as zero
     * @param padStr
     *            the String to pad the new String with, must not be null or
     *            empty
     * @return centered String, <code>null</code> if null String input
     * @throws IllegalArgumentException
     *             if padStr is <code>null</code> or empty
     */
    public static String center(String str, int size, String padStr) {
        if (str == null || size <= 0) {
            return str;
        }
        if (isEmpty(padStr)) {
            padStr = " ";
        }
        int strLen = str.length();
        int pads = size - strLen;
        if (pads <= 0) {
            return str;
        }
        str = leftPad(str, strLen + pads / 2, padStr);
        str = rightPad(str, size, padStr);
        return str;
    }

    // Case conversion
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Converts a String to upper case as per {@link String#toUpperCase()}.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * upperCase(null)  = null
     * upperCase("")    = ""
     * upperCase("aBc") = "ABC"
     * </pre>
     * 
     * <p>
     * <strong>Note:</strong> As described in the documentation for
     * {@link String#toUpperCase()}, the result of this method is affected by
     * the current locale. For platform-independent case transformations, the
     * method {@link #lowerCase(String, Locale)} should be used with a specific
     * locale (e.g. {@link Locale#ENGLISH}).
     * </p>
     * 
     * @param str
     *            the String to upper case, may be null
     * @return the upper cased String, <code>null</code> if null String input
     */
    public static String upperCase(String str) {
        if (str == null) {
            return null;
        }
        return str.toUpperCase();
    }

    /**
     * <p>
     * Converts a String to upper case as per {@link String#toUpperCase(Locale)}
     * .
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * upperCase(null, Locale.ENGLISH)  = null
     * upperCase("", Locale.ENGLISH)    = ""
     * upperCase("aBc", Locale.ENGLISH) = "ABC"
     * </pre>
     * 
     * @param str
     *            the String to upper case, may be null
     * @param locale
     *            the locale that defines the case transformation rules, must
     *            not be null
     * @return the upper cased String, <code>null</code> if null String input
     * @since 2.5
     */
    public static String upperCase(String str, Locale locale) {
        if (str == null) {
            return null;
        }
        return str.toUpperCase(locale);
    }

    /**
     * <p>
     * Converts a String to lower case as per {@link String#toLowerCase()}.
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * lowerCase(null)  = null
     * lowerCase("")    = ""
     * lowerCase("aBc") = "abc"
     * </pre>
     * 
     * <p>
     * <strong>Note:</strong> As described in the documentation for
     * {@link String#toLowerCase()}, the result of this method is affected by
     * the current locale. For platform-independent case transformations, the
     * method {@link #lowerCase(String, Locale)} should be used with a specific
     * locale (e.g. {@link Locale#ENGLISH}).
     * </p>
     * 
     * @param str
     *            the String to lower case, may be null
     * @return the lower cased String, <code>null</code> if null String input
     */
    public static String lowerCase(String str) {
        if (str == null) {
            return null;
        }
        return str.toLowerCase();
    }

    /**
     * <p>
     * Converts a String to lower case as per {@link String#toLowerCase(Locale)}
     * .
     * </p>
     * 
     * <p>
     * A <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * lowerCase(null, Locale.ENGLISH)  = null
     * lowerCase("", Locale.ENGLISH)    = ""
     * lowerCase("aBc", Locale.ENGLISH) = "abc"
     * </pre>
     * 
     * @param str
     *            the String to lower case, may be null
     * @param locale
     *            the locale that defines the case transformation rules, must
     *            not be null
     * @return the lower cased String, <code>null</code> if null String input
     * @since 2.5
     */
    public static String lowerCase(String str, Locale locale) {
        if (str == null) {
            return null;
        }
        return str.toLowerCase(locale);
    }

    /**
     * <p>
     * Capitalizes a String changing the first letter to title case as per
     * {@link Character#toTitleCase(char)}. No other letters are changed.
     * </p>
     * 
     * <p>
     * For a word based algorithm, see
     * {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}. A
     * <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * capitalize(null)  = null
     * capitalize("")    = ""
     * capitalize("cat") = "Cat"
     * capitalize("cAt") = "CAt"
     * </pre>
     * 
     * @param cs
     *            the String to capitalize, may be null
     * @return the capitalized String, <code>null</code> if null String input
     * @see org.apache.commons.lang3.text.WordUtils#capitalize(String)
     * @see #uncapitalize(CharSequence)
     * @since 2.0
     * @since 3.0 Changed signature from capitalize(String) to
     *        capitalize(CharSequence)
     */
    public static String capitalize(CharSequence cs) {
        if (cs == null) {
            return null;
        }
        int strLen;
        if ((strLen = cs.length()) == 0) {
            return cs.toString();
        }
        return new StringBuilder(strLen)
                .append(Character.toTitleCase(cs.charAt(0)))
                .append(subSequence(cs, 1)).toString();
    }

    /**
     * <p>
     * Uncapitalizes a CharSequence changing the first letter to title case as
     * per {@link Character#toLowerCase(char)}. No other letters are changed.
     * </p>
     * 
     * <p>
     * For a word based algorithm, see
     * {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}. A
     * <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * uncapitalize(null)  = null
     * uncapitalize("")    = ""
     * uncapitalize("Cat") = "cat"
     * uncapitalize("CAT") = "cAT"
     * </pre>
     * 
     * @param cs
     *            the String to uncapitalize, may be null
     * @return the uncapitalized String, <code>null</code> if null String input
     * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String)
     * @see #capitalize(CharSequence)
     * @since 2.0
     * @since 3.0 Changed signature from uncapitalize(String) to
     *        uncapitalize(CharSequence)
     */
    public static String uncapitalize(CharSequence cs) {
        if (cs == null) {
            return null;
        }
        int strLen;
        if ((strLen = cs.length()) == 0) {
            return cs.toString();
        }
        return new StringBuilder(strLen)
                .append(Character.toLowerCase(cs.charAt(0)))
                .append(subSequence(cs, 1)).toString();
    }

    /**
     * <p>
     * Swaps the case of a String changing upper and title case to lower case,
     * and lower case to upper case.
     * </p>
     * 
     * <ul>
     * <li>Upper case character converts to Lower case</li>
     * <li>Title case character converts to Lower case</li>
     * <li>Lower case character converts to Upper case</li>
     * </ul>
     * 
     * <p>
     * For a word based algorithm, see
     * {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}. A
     * <code>null</code> input String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * swapCase(null)                 = null
     * swapCase("")                   = ""
     * swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
     * </pre>
     * 
     * <p>
     * NOTE: This method changed in Lang version 2.0. It no longer performs a
     * word based algorithm. If you only use ASCII, you will notice no change.
     * That functionality is available in
     * org.apache.commons.lang3.text.WordUtils.
     * </p>
     * 
     * @param str
     *            the String to swap case, may be null
     * @return the changed String, <code>null</code> if null String input
     */
    public static String swapCase(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return str;
        }
        StringBuilder buffer = new StringBuilder(strLen);

        char ch = 0;
        for (int i = 0; i < strLen; i++) {
            ch = str.charAt(i);
            if (Character.isUpperCase(ch)) {
                ch = Character.toLowerCase(ch);
            } else if (Character.isTitleCase(ch)) {
                ch = Character.toLowerCase(ch);
            } else if (Character.isLowerCase(ch)) {
                ch = Character.toUpperCase(ch);
            }
            buffer.append(ch);
        }
        return buffer.toString();
    }

    // Count matches
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Counts how many times the substring appears in the larger String.
     * </p>
     * 
     * <p>
     * A <code>null</code> or empty ("") String input returns <code>0</code>.
     * </p>
     * 
     * <pre>
     * countMatches(null, *)       = 0
     * countMatches("", *)         = 0
     * countMatches("abba", null)  = 0
     * countMatches("abba", "")    = 0
     * countMatches("abba", "a")   = 2
     * countMatches("abba", "ab")  = 1
     * countMatches("abba", "xxx") = 0
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param sub
     *            the substring to count, may be null
     * @return the number of occurrences, 0 if either String is
     *         <code>null</code>
     */
    public static int countMatches(String str, String sub) {
        if (isEmpty(str) || isEmpty(sub)) {
            return 0;
        }
        int count = 0;
        int idx = 0;
        while ((idx = str.indexOf(sub, idx)) != INDEX_NOT_FOUND) {
            count++;
            idx += sub.length();
        }
        return count;
    }

    // Character Tests
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if the CharSequence contains only unicode letters.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isAlpha(null)   = false
     * isAlpha("")     = true
     * isAlpha("  ")   = false
     * isAlpha("abc")  = true
     * isAlpha("ab2c") = false
     * isAlpha("ab-c") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains letters, and is non-null
     * @since 3.0 Changed signature from isAlpha(String) to
     *        isAlpha(CharSequence)
     */
    public static boolean isAlpha(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isLetter(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only unicode letters and space (' ').
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code> An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isAlphaSpace(null)   = false
     * isAlphaSpace("")     = true
     * isAlphaSpace("  ")   = true
     * isAlphaSpace("abc")  = true
     * isAlphaSpace("ab c") = true
     * isAlphaSpace("ab2c") = false
     * isAlphaSpace("ab-c") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains letters and space, and is
     *         non-null
     * @since 3.0 Changed signature from isAlphaSpace(String) to
     *        isAlphaSpace(CharSequence)
     */
    public static boolean isAlphaSpace(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isLetter(cs.charAt(i)) == false)
                    && (cs.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only unicode letters or digits.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isAlphanumeric(null)   = false
     * isAlphanumeric("")     = true
     * isAlphanumeric("  ")   = false
     * isAlphanumeric("abc")  = true
     * isAlphanumeric("ab c") = false
     * isAlphanumeric("ab2c") = true
     * isAlphanumeric("ab-c") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains letters or digits, and is
     *         non-null
     * @since 3.0 Changed signature from isAlphanumeric(String) to
     *        isAlphanumeric(CharSequence)
     */
    public static boolean isAlphanumeric(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isLetterOrDigit(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only unicode letters, digits or space
     * (<code>' '</code>).
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isAlphanumeric(null)   = false
     * isAlphanumeric("")     = true
     * isAlphanumeric("  ")   = true
     * isAlphanumeric("abc")  = true
     * isAlphanumeric("ab c") = true
     * isAlphanumeric("ab2c") = true
     * isAlphanumeric("ab-c") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains letters, digits or space, and
     *         is non-null
     * @since 3.0 Changed signature from isAlphanumericSpace(String) to
     *        isAlphanumericSpace(CharSequence)
     */
    public static boolean isAlphanumericSpace(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isLetterOrDigit(cs.charAt(i)) == false)
                    && (cs.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only ASCII printable characters.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isAsciiPrintable(null)     = false
     * isAsciiPrintable("")       = true
     * isAsciiPrintable(" ")      = true
     * isAsciiPrintable("Ceki")   = true
     * isAsciiPrintable("ab2c")   = true
     * isAsciiPrintable("!ab-c~") = true
     * isAsciiPrintable("\u0020") = true
     * isAsciiPrintable("\u0021") = true
     * isAsciiPrintable("\u007e") = true
     * isAsciiPrintable("\u007f") = false
     * isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if every character is in the range 32 thru 126
     * @since 2.1
     * @since 3.0 Changed signature from isAsciiPrintable(String) to
     *        isAsciiPrintable(CharSequence)
     */
    public static boolean isAsciiPrintable(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (isAsciiPrintable(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only unicode digits. A decimal point
     * is not a unicode digit and returns false.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isNumeric(null)   = false
     * isNumeric("")     = true
     * isNumeric("  ")   = false
     * isNumeric("123")  = true
     * isNumeric("12 3") = false
     * isNumeric("ab2c") = false
     * isNumeric("12-3") = false
     * isNumeric("12.3") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains digits, and is non-null
     * @since 3.0 Changed signature from isNumeric(String) to
     *        isNumeric(CharSequence)
     */
    public static boolean isNumeric(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isDigit(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only unicode digits or space (
     * <code>' '</code>). A decimal point is not a unicode digit and returns
     * false.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isNumeric(null)   = false
     * isNumeric("")     = true
     * isNumeric("  ")   = true
     * isNumeric("123")  = true
     * isNumeric("12 3") = true
     * isNumeric("ab2c") = false
     * isNumeric("12-3") = false
     * isNumeric("12.3") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains digits or space, and is
     *         non-null
     * @since 3.0 Changed signature from isNumericSpace(String) to
     *        isNumericSpace(CharSequence)
     */
    public static boolean isNumericSpace(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isDigit(cs.charAt(i)) == false)
                    && (cs.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only whitespace.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>true</code>.
     * </p>
     * 
     * <pre>
     * isWhitespace(null)   = false
     * isWhitespace("")     = true
     * isWhitespace("  ")   = true
     * isWhitespace("abc")  = false
     * isWhitespace("ab2c") = false
     * isWhitespace("ab-c") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains whitespace, and is non-null
     * @since 2.0
     * @since 3.0 Changed signature from isWhitespace(String) to
     *        isWhitespace(CharSequence)
     */
    public static boolean isWhitespace(CharSequence cs) {
        if (cs == null) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isWhitespace(cs.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only lowercase characters.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty CharSequence
     * (length()=0) will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * isAllLowerCase(null)   = false
     * isAllLowerCase("")     = false
     * isAllLowerCase("  ")   = false
     * isAllLowerCase("abc")  = true
     * isAllLowerCase("abC") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains lowercase characters, and is
     *         non-null
     * @since 2.5
     * @since 3.0 Changed signature from isAllLowerCase(String) to
     *        isAllLowerCase(CharSequence)
     */
    public static boolean isAllLowerCase(CharSequence cs) {
        if (cs == null || isEmpty(cs)) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isLowerCase(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if the CharSequence contains only uppercase characters.
     * </p>
     * 
     * <p>
     * <code>null</code> will return <code>false</code>. An empty String
     * (length()=0) will return <code>false</code>.
     * </p>
     * 
     * <pre>
     * isAllUpperCase(null)   = false
     * isAllUpperCase("")     = false
     * isAllUpperCase("  ")   = false
     * isAllUpperCase("ABC")  = true
     * isAllUpperCase("aBC") = false
     * </pre>
     * 
     * @param cs
     *            the CharSequence to check, may be null
     * @return <code>true</code> if only contains uppercase characters, and is
     *         non-null
     * @since 2.5
     * @since 3.0 Changed signature from isAllUpperCase(String) to
     *        isAllUpperCase(CharSequence)
     */
    public static boolean isAllUpperCase(CharSequence cs) {
        if (cs == null || isEmpty(cs)) {
            return false;
        }
        int sz = cs.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isUpperCase(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    // Defaults
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Returns either the passed in String, or if the String is
     * <code>null</code>, an empty String ("").
     * </p>
     * 
     * <pre>
     * defaultString(null)  = ""
     * defaultString("")    = ""
     * defaultString("bat") = "bat"
     * </pre>
     * 
     * @see ObjectUtils#charToString(Object)
     * @see String#valueOf(Object)
     * @param str
     *            the String to check, may be null
     * @return the passed in String, or the empty String if it was
     *         <code>null</code>
     */
    public static String defaultString(String str) {
        return str == null ? EMPTY_STRING : str;
    }

    /**
     * <p>
     * Returns either the passed in String, or if the String is
     * <code>null</code>, the value of <code>defaultStr</code>.
     * </p>
     * 
     * <pre>
     * defaultString(null, "NULL")  = "NULL"
     * defaultString("", "NULL")    = ""
     * defaultString("bat", "NULL") = "bat"
     * </pre>
     * 
     * @see ObjectUtils#objectToString(Object,String)
     * @see String#valueOf(Object)
     * @param str
     *            the String to check, may be null
     * @param defaultStr
     *            the default String to return if the input is <code>null</code>
     *            , may be null
     * @return the passed in String, or the default if it was <code>null</code>
     */
    public static String defaultString(String str, String defaultStr) {
        return str == null ? defaultStr : str;
    }

    /**
     * <p>
     * Returns either the passed in CharSequence, or if the CharSequence is
     * empty or <code>null</code>, the value of <code>defaultStr</code>.
     * </p>
     * 
     * <pre>
     * defaultIfEmpty(null, "NULL")  = "NULL"
     * defaultIfEmpty("", "NULL")    = "NULL"
     * defaultIfEmpty("bat", "NULL") = "bat"
     * defaultIfEmpty("", null)      = null
     * </pre>
     * 
     * @param <T>
     *            the specific kind of CharSequence
     * @param str
     *            the CharSequence to check, may be null
     * @param defaultStr
     *            the default CharSequence to return if the input is empty ("")
     *            or <code>null</code>, may be null
     * @return the passed in CharSequence, or the default
     * @see defaultString(String, String)
     */
    public static <T extends CharSequence> T defaultIfEmpty(T str, T defaultStr) {
        return isEmpty(str) ? defaultStr : str;
    }

    // Reversing
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Reverses a String as per {@link StringBuilder#reverse()}.
     * </p>
     * 
     * <p>
     * A <code>null</code> String returns <code>null</code>.
     * </p>
     * 
     * <pre>
     * reverse(null)  = null
     * reverse("")    = ""
     * reverse("bat") = "tab"
     * </pre>
     * 
     * @param str
     *            the String to reverse, may be null
     * @return the reversed String, <code>null</code> if null String input
     */
    public static String reverse(String str) {
        if (str == null) {
            return null;
        }
        return new StringBuilder(str).reverse().toString();
    }

    /**
     * <p>
     * Reverses a String that is delimited by a specific character.
     * </p>
     * 
     * <p>
     * The Strings between the delimiters are not reversed. Thus
     * java.lang.String becomes String.lang.java (if the delimiter is
     * <code>'.'</code>).
     * </p>
     * 
     * <pre>
     * reverseDelimited(null, *)      = null
     * reverseDelimited("", *)        = ""
     * reverseDelimited("a.b.c", 'x') = "a.b.c"
     * reverseDelimited("a.b.c", ".") = "c.b.a"
     * </pre>
     * 
     * @param str
     *            the String to reverse, may be null
     * @param separatorChar
     *            the separator character to use
     * @return the reversed String, <code>null</code> if null String input
     * @since 2.0
     */
    public static String reverseDelimited(String str, char separatorChar) {
        if (str == null) {
            return null;
        }
        // could implement manually, but simple way is to reuse other,
        // probably slower, methods.
        String[] strs = split(str, separatorChar);
        reverse(strs);
        return join(strs, separatorChar);
    }

    // Abbreviating
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Abbreviates a String using ellipses. This will turn
     * "Now is the time for all good men" into "Now is the time for..."
     * </p>
     * 
     * <p>
     * Specifically:
     * <ul>
     * <li>If <code>str</code> is less than <code>maxWidth</code> characters
     * long, return it.</li>
     * <li>Else abbreviate it to <code>(substring(str, 0, max-3) + "...")</code>
     * .</li>
     * <li>If <code>maxWidth</code> is less than <code>4</code>, throw an
     * <code>IllegalArgumentException</code>.</li>
     * <li>In no case will it return a String of length greater than
     * <code>maxWidth</code>.</li>
     * </ul>
     * </p>
     * 
     * <pre>
     * abbreviate(null, *)      = null
     * abbreviate("", 4)        = ""
     * abbreviate("abcdefg", 6) = "abc..."
     * abbreviate("abcdefg", 7) = "abcdefg"
     * abbreviate("abcdefg", 8) = "abcdefg"
     * abbreviate("abcdefg", 4) = "a..."
     * abbreviate("abcdefg", 3) = IllegalArgumentException
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param maxWidth
     *            maximum length of result String, must be at least 4
     * @return abbreviated String, <code>null</code> if null String input
     * @throws IllegalArgumentException
     *             if the width is too small
     * @since 2.0
     */
    public static String abbreviate(String str, int maxWidth) {
        return abbreviate(str, 0, maxWidth);
    }

    /**
     * <p>
     * Abbreviates a String using ellipses. This will turn
     * "Now is the time for all good men" into "...is the time for..."
     * </p>
     * 
     * <p>
     * Works like <code>abbreviate(String, int)</code>, but allows you to
     * specify a "left edge" offset. Note that this left edge is not necessarily
     * going to be the leftmost character in the result, or the first character
     * following the ellipses, but it will appear somewhere in the result.
     * 
     * <p>
     * In no case will it return a String of length greater than
     * <code>maxWidth</code>.
     * </p>
     * 
     * <pre>
     * abbreviate(null, *, *)                = null
     * abbreviate("", 0, 4)                  = ""
     * abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
     * abbreviate("abcdefghijklmno", 0, 10)  = "abcdefg..."
     * abbreviate("abcdefghijklmno", 1, 10)  = "abcdefg..."
     * abbreviate("abcdefghijklmno", 4, 10)  = "abcdefg..."
     * abbreviate("abcdefghijklmno", 5, 10)  = "...fghi..."
     * abbreviate("abcdefghijklmno", 6, 10)  = "...ghij..."
     * abbreviate("abcdefghijklmno", 8, 10)  = "...ijklmno"
     * abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
     * abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
     * abbreviate("abcdefghij", 0, 3)        = IllegalArgumentException
     * abbreviate("abcdefghij", 5, 6)        = IllegalArgumentException
     * </pre>
     * 
     * @param str
     *            the String to check, may be null
     * @param offset
     *            left edge of source String
     * @param maxWidth
     *            maximum length of result String, must be at least 4
     * @return abbreviated String, <code>null</code> if null String input
     * @throws IllegalArgumentException
     *             if the width is too small
     * @since 2.0
     */
    public static String abbreviate(String str, int offset, int maxWidth) {
        if (str == null) {
            return null;
        }
        if (maxWidth < 4) {
            throw new IllegalArgumentException(
                    "Minimum abbreviation width is 4");
        }
        if (str.length() <= maxWidth) {
            return str;
        }
        if (offset > str.length()) {
            offset = str.length();
        }
        if ((str.length() - offset) < (maxWidth - 3)) {
            offset = str.length() - (maxWidth - 3);
        }
        final String abrevMarker = "...";
        if (offset <= 4) {
            return str.substring(0, maxWidth - 3) + abrevMarker;
        }
        if (maxWidth < 7) {
            throw new IllegalArgumentException(
                    "Minimum abbreviation width with offset is 7");
        }
        if ((offset + (maxWidth - 3)) < str.length()) {
            return abrevMarker
                    + abbreviate(str.substring(offset), maxWidth - 3);
        }
        return abrevMarker + str.substring(str.length() - (maxWidth - 3));
    }

    /**
     * <p>
     * Abbreviates a String to the length passed, replacing the middle
     * characters with the supplied replacement String.
     * </p>
     * 
     * <p>
     * This abbreviation only occurs if the following criteria is met:
     * <ul>
     * <li>Neither the String for abbreviation nor the replacement String are
     * null or empty</li>
     * <li>The length to truncate to is less than the length of the supplied
     * String</li>
     * <li>The length to truncate to is greater than 0</li>
     * <li>The abbreviated String will have enough room for the length supplied
     * replacement String and the first and last characters of the supplied
     * String for abbreviation</li>
     * </ul>
     * Otherwise, the returned String will be the same as the supplied String
     * for abbreviation.
     * </p>
     * 
     * <pre>
     * abbreviateMiddle(null, null, 0)      = null
     * abbreviateMiddle("abc", null, 0)      = "abc"
     * abbreviateMiddle("abc", ".", 0)      = "abc"
     * abbreviateMiddle("abc", ".", 3)      = "abc"
     * abbreviateMiddle("abcdef", ".", 4)     = "ab.f"
     * </pre>
     * 
     * @param str
     *            the String to abbreviate, may be null
     * @param middle
     *            the String to replace the middle characters with, may be null
     * @param length
     *            the length to abbreviate <code>str</code> to.
     * @return the abbreviated String if the above criteria is met, or the
     *         original String supplied for abbreviation.
     * @since 2.5
     */
    public static String abbreviateMiddle(String str, String middle, int length) {
        if (isEmpty(str) || isEmpty(middle)) {
            return str;
        }

        if (length >= str.length() || length < (middle.length() + 2)) {
            return str;
        }

        int targetSting = length - middle.length();
        int startOffset = targetSting / 2 + targetSting % 2;
        int endOffset = str.length() - targetSting / 2;

        StringBuilder builder = new StringBuilder(length);
        builder.append(str.substring(0, startOffset));
        builder.append(middle);
        builder.append(str.substring(endOffset));

        return builder.toString();
    }

    // Difference
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Compares two Strings, and returns the portion where they differ. (More
     * precisely, return the remainder of the second String, starting from where
     * it's different from the first.)
     * </p>
     * 
     * <p>
     * For example,
     * <code>difference("i am a machine", "i am a robot") -> "robot"</code>.
     * </p>
     * 
     * <pre>
     * difference(null, null) = null
     * difference("", "") = ""
     * difference("", "abc") = "abc"
     * difference("abc", "") = ""
     * difference("abc", "abc") = ""
     * difference("ab", "abxyz") = "xyz"
     * difference("abcde", "abxyz") = "xyz"
     * difference("abcde", "xyz") = "xyz"
     * </pre>
     * 
     * @param str1
     *            the first String, may be null
     * @param str2
     *            the second String, may be null
     * @return the portion of str2 where it differs from str1; returns the empty
     *         String if they are equal
     * @since 2.0
     */
    public static String difference(String str1, String str2) {
        if (str1 == null) {
            return str2;
        }
        if (str2 == null) {
            return str1;
        }
        int at = indexOfDifference(str1, str2);
        if (at == INDEX_NOT_FOUND) {
            return EMPTY_STRING;
        }
        return str2.substring(at);
    }

    /**
     * <p>
     * Compares two CharSequences, and returns the index at which the
     * CharSequences begin to differ.
     * </p>
     * 
     * <p>
     * For example,
     * <code>indexOfDifference("i am a machine", "i am a robot") -> 7</code>
     * </p>
     * 
     * <pre>
     * indexOfDifference(null, null) = -1
     * indexOfDifference("", "") = -1
     * indexOfDifference("", "abc") = 0
     * indexOfDifference("abc", "") = 0
     * indexOfDifference("abc", "abc") = -1
     * indexOfDifference("ab", "abxyz") = 2
     * indexOfDifference("abcde", "abxyz") = 2
     * indexOfDifference("abcde", "xyz") = 0
     * </pre>
     * 
     * @param cs1
     *            the first CharSequence, may be null
     * @param cs2
     *            the second CharSequence, may be null
     * @return the index where cs1 and cs2 begin to differ; -1 if they are equal
     * @since 2.0
     * @since 3.0 Changed signature from indexOfDifference(String, String) to
     *        indexOfDifference(CharSequence, CharSequence)
     */
    public static int indexOfDifference(CharSequence cs1, CharSequence cs2) {
        if (cs1 == cs2) {
            return INDEX_NOT_FOUND;
        }
        if (cs1 == null || cs2 == null) {
            return 0;
        }
        int i;
        for (i = 0; i < cs1.length() && i < cs2.length(); ++i) {
            if (cs1.charAt(i) != cs2.charAt(i)) {
                break;
            }
        }
        if (i < cs2.length() || i < cs1.length()) {
            return i;
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Compares all CharSequences in an array and returns the index at which the
     * CharSequences begin to differ.
     * </p>
     * 
     * <p>
     * For example,
     * <code>indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7</code>
     * </p>
     * 
     * <pre>
     * indexOfDifference(null) = -1
     * indexOfDifference(new String[] {}) = -1
     * indexOfDifference(new String[] {"abc"}) = -1
     * indexOfDifference(new String[] {null, null}) = -1
     * indexOfDifference(new String[] {"", ""}) = -1
     * indexOfDifference(new String[] {"", null}) = 0
     * indexOfDifference(new String[] {"abc", null, null}) = 0
     * indexOfDifference(new String[] {null, null, "abc"}) = 0
     * indexOfDifference(new String[] {"", "abc"}) = 0
     * indexOfDifference(new String[] {"abc", ""}) = 0
     * indexOfDifference(new String[] {"abc", "abc"}) = -1
     * indexOfDifference(new String[] {"abc", "a"}) = 1
     * indexOfDifference(new String[] {"ab", "abxyz"}) = 2
     * indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
     * indexOfDifference(new String[] {"abcde", "xyz"}) = 0
     * indexOfDifference(new String[] {"xyz", "abcde"}) = 0
     * indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
     * </pre>
     * 
     * @param css
     *            array of CharSequences, entries may be null
     * @return the index where the strings begin to differ; -1 if they are all
     *         equal
     * @since 2.4
     * @since 3.0 Changed signature from indexOfDifference(String...) to
     *        indexOfDifference(CharSequence...)
     */
    public static int indexOfDifference(CharSequence... css) {
        if (css == null || css.length <= 1) {
            return INDEX_NOT_FOUND;
        }
        boolean anyStringNull = false;
        boolean allStringsNull = true;
        int arrayLen = css.length;
        int shortestStrLen = Integer.MAX_VALUE;
        int longestStrLen = 0;

        // find the min and max string lengths; this avoids checking to make
        // sure we are not exceeding the length of the string each time through
        // the bottom loop.
        for (int i = 0; i < arrayLen; i++) {
            if (css[i] == null) {
                anyStringNull = true;
                shortestStrLen = 0;
            } else {
                allStringsNull = false;
                shortestStrLen = Math.min(css[i].length(), shortestStrLen);
                longestStrLen = Math.max(css[i].length(), longestStrLen);
            }
        }

        // handle lists containing all nulls or all empty strings
        if (allStringsNull || (longestStrLen == 0 && !anyStringNull)) {
            return INDEX_NOT_FOUND;
        }

        // handle lists containing some nulls or some empty strings
        if (shortestStrLen == 0) {
            return 0;
        }

        // find the position with the first difference across all strings
        int firstDiff = -1;
        for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) {
            char comparisonChar = css[0].charAt(stringPos);
            for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) {
                if (css[arrayPos].charAt(stringPos) != comparisonChar) {
                    firstDiff = stringPos;
                    break;
                }
            }
            if (firstDiff != -1) {
                break;
            }
        }

        if (firstDiff == -1 && shortestStrLen != longestStrLen) {
            // we compared all of the characters up to the length of the
            // shortest string and didn't find a match, but the string lengths
            // vary, so return the length of the shortest string.
            return shortestStrLen;
        }
        return firstDiff;
    }

    /**
     * <p>
     * Compares all Strings in an array and returns the initial sequence of
     * characters that is common to all of them.
     * </p>
     * 
     * <p>
     * For example,
     * <code>getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "</code>
     * </p>
     * 
     * <pre>
     * getCommonPrefix(null) = ""
     * getCommonPrefix(new String[] {}) = ""
     * getCommonPrefix(new String[] {"abc"}) = "abc"
     * getCommonPrefix(new String[] {null, null}) = ""
     * getCommonPrefix(new String[] {"", ""}) = ""
     * getCommonPrefix(new String[] {"", null}) = ""
     * getCommonPrefix(new String[] {"abc", null, null}) = ""
     * getCommonPrefix(new String[] {null, null, "abc"}) = ""
     * getCommonPrefix(new String[] {"", "abc"}) = ""
     * getCommonPrefix(new String[] {"abc", ""}) = ""
     * getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
     * getCommonPrefix(new String[] {"abc", "a"}) = "a"
     * getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
     * getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
     * getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
     * getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
     * getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
     * </pre>
     * 
     * @param strs
     *            array of String objects, entries may be null
     * @return the initial sequence of characters that are common to all Strings
     *         in the array; empty String if the array is null, the elements are
     *         all null or if there is no common prefix.
     * @since 2.4
     */
    public static String getCommonPrefix(String... strs) {
        if (strs == null || strs.length == 0) {
            return EMPTY_STRING;
        }
        int smallestIndexOfDiff = indexOfDifference(strs);
        if (smallestIndexOfDiff == INDEX_NOT_FOUND) {
            // all strings were identical
            if (strs[0] == null) {
                return EMPTY_STRING;
            }
            return strs[0];
        } else if (smallestIndexOfDiff == 0) {
            // there were no common initial characters
            return EMPTY_STRING;
        } else {
            // we found a common initial character sequence
            return strs[0].substring(0, smallestIndexOfDiff);
        }
    }

    // Misc
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Find the Levenshtein distance between two Strings.
     * </p>
     * 
     * <p>
     * This is the number of changes needed to change one String into another,
     * where each change is a single character modification (deletion, insertion
     * or substitution).
     * </p>
     * 
     * <p>
     * The previous implementation of the Levenshtein distance algorithm was
     * from <a
     * href="http://www.merriampark.com/ld.htm">http://www.merriampark.com
     * /ld.htm</a>
     * </p>
     * 
     * <p>
     * Chas Emerick has written an implementation in Java, which avoids an
     * OutOfMemoryError which can occur when my Java implementation is used with
     * very large strings.<br>
     * This implementation of the Levenshtein distance algorithm is from <a
     * href="http://www.merriampark.com/ldjava.htm">http://www.merriampark.com/
     * ldjava.htm</a>
     * </p>
     * 
     * <pre>
     * getLevenshteinDistance(null, *)             = IllegalArgumentException
     * getLevenshteinDistance(*, null)             = IllegalArgumentException
     * getLevenshteinDistance("","")               = 0
     * getLevenshteinDistance("","a")              = 1
     * getLevenshteinDistance("aaapppp", "")       = 7
     * getLevenshteinDistance("frog", "fog")       = 1
     * getLevenshteinDistance("fly", "ant")        = 3
     * getLevenshteinDistance("elephant", "hippo") = 7
     * getLevenshteinDistance("hippo", "elephant") = 7
     * getLevenshteinDistance("hippo", "zzzzzzzz") = 8
     * getLevenshteinDistance("hello", "hallo")    = 1
     * </pre>
     * 
     * @param s
     *            the first String, must not be null
     * @param t
     *            the second String, must not be null
     * @return result distance
     * @throws IllegalArgumentException
     *             if either String input <code>null</code>
     * @since 3.0 Changed signature from getLevenshteinDistance(String, String)
     *        to getLevenshteinDistance(CharSequence, CharSequence)
     */
    public static int getLevenshteinDistance(CharSequence s, CharSequence t) {
        if (s == null || t == null) {
            throw new IllegalArgumentException("Strings must not be null");
        }

        /*
         * The difference between this impl. and the previous is that, rather
         * than creating and retaining a matrix of size s.length()+1 by
         * t.length()+1, we maintain two single-dimensional arrays of length
         * s.length()+1. The first, d, is the 'current working' distance array
         * that maintains the newest distance cost counts as we iterate through
         * the characters of String s. Each time we increment the index of
         * String t we are comparing, d is copied to p, the second int[]. Doing
         * so allows us to retain the previous cost counts as required by the
         * algorithm (taking the minimum of the cost count to the left, up one,
         * and diagonally up and to the left of the current cost count being
         * calculated). (Note that the arrays aren't really copied anymore, just
         * switched...this is clearly much better than cloning an array or doing
         * a System.arraycopy() each time through the outer loop.)
         * 
         * Effectively, the difference between the two implementations is this
         * one does not cause an out of memory condition when calculating the LD
         * over two very large strings.
         */

        int n = s.length(); // length of s
        int m = t.length(); // length of t

        if (n == 0) {
            return m;
        } else if (m == 0) {
            return n;
        }

        if (n > m) {
            // swap the input strings to consume less memory
            CharSequence tmp = s;
            s = t;
            t = tmp;
            n = m;
            m = t.length();
        }

        int p[] = new int[n + 1]; // 'previous' cost array, horizontally
        int d[] = new int[n + 1]; // cost array, horizontally
        int _d[]; // placeholder to assist in swapping p and d

        // indexes into strings s and t
        int i; // iterates through s
        int j; // iterates through t

        char t_j; // jth character of t

        int cost; // cost

        for (i = 0; i <= n; i++) {
            p[i] = i;
        }

        for (j = 1; j <= m; j++) {
            t_j = t.charAt(j - 1);
            d[0] = j;

            for (i = 1; i <= n; i++) {
                cost = s.charAt(i - 1) == t_j ? 0 : 1;
                // minimum of cell to the left+1, to the top+1, diagonally left
                // and up +cost
                d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1]
                        + cost);
            }

            // copy current distance counts to 'previous row' distance counts
            _d = p;
            p = d;
            d = _d;
        }

        // our last action in the above loop was to switch d and p, so p now
        // actually has the most recent cost counts
        return p[n];
    }

    // startsWith
    // -----------------------------------------------------------------------

    /**
     * <p>
     * Check if a String starts with a specified prefix.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered to be equal. The comparison is case sensitive.
     * </p>
     * 
     * <pre>
     * startsWith(null, null)      = true
     * startsWith(null, "abc")     = false
     * startsWith("abcdef", null)  = false
     * startsWith("abcdef", "abc") = true
     * startsWith("ABCDEF", "abc") = false
     * </pre>
     * 
     * @see java.lang.String#startsWith(String)
     * @param str
     *            the String to check, may be null
     * @param prefix
     *            the prefix to find, may be null
     * @return <code>true</code> if the String starts with the prefix, case
     *         sensitive, or both <code>null</code>
     * @since 2.4
     */
    public static boolean startsWith(String str, String prefix) {
        return startsWith(str, prefix, false);
    }

    /**
     * <p>
     * Case insensitive check if a String starts with a specified prefix.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered to be equal. The comparison is case
     * insensitive.
     * </p>
     * 
     * <pre>
     * startsWithIgnoreCase(null, null)      = true
     * startsWithIgnoreCase(null, "abc")     = false
     * startsWithIgnoreCase("abcdef", null)  = false
     * startsWithIgnoreCase("abcdef", "abc") = true
     * startsWithIgnoreCase("ABCDEF", "abc") = true
     * </pre>
     * 
     * @see java.lang.String#startsWith(String)
     * @param str
     *            the String to check, may be null
     * @param prefix
     *            the prefix to find, may be null
     * @return <code>true</code> if the String starts with the prefix, case
     *         insensitive, or both <code>null</code>
     * @since 2.4
     */
    public static boolean startsWithIgnoreCase(String str, String prefix) {
        return startsWith(str, prefix, true);
    }

    /**
     * <p>
     * Check if a String starts with a specified prefix (optionally case
     * insensitive).
     * </p>
     * 
     * @see java.lang.String#startsWith(String)
     * @param str
     *            the String to check, may be null
     * @param prefix
     *            the prefix to find, may be null
     * @param ignoreCase
     *            inidicates whether the compare should ignore case (case
     *            insensitive) or not.
     * @return <code>true</code> if the String starts with the prefix or both
     *         <code>null</code>
     */
    private static boolean startsWith(String str, String prefix,
            boolean ignoreCase) {
        if (str == null || prefix == null) {
            return (str == null && prefix == null);
        }
        if (prefix.length() > str.length()) {
            return false;
        }
        return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
    }

    /**
     * <p>
     * Check if a String starts with any of an array of specified strings.
     * </p>
     * 
     * <pre>
     * startsWithAny(null, null)      = false
     * startsWithAny(null, new String[] {"abc"})  = false
     * startsWithAny("abcxyz", null)     = false
     * startsWithAny("abcxyz", new String[] {""}) = false
     * startsWithAny("abcxyz", new String[] {"abc"}) = true
     * startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
     * </pre>
     * 
     * @param string
     *            the String to check, may be null
     * @param searchStrings
     *            the Strings to find, may be null or empty
     * @return <code>true</code> if the String starts with any of the the
     *         prefixes, case insensitive, or both <code>null</code>
     * @since 2.5
     */
    public static boolean startsWithAny(String string, String... searchStrings) {
        if (isEmpty(string) || isEmpty(searchStrings)) {
            return false;
        }
        for (int i = 0; i < searchStrings.length; i++) {
            String searchString = searchStrings[i];
            if (startsWith(string, searchString)) {
                return true;
            }
        }
        return false;
    }

    // endsWith
    // -----------------------------------------------------------------------

    /**
     * <p>
     * Check if a String ends with a specified suffix.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered to be equal. The comparison is case sensitive.
     * </p>
     * 
     * <pre>
     * endsWith(null, null)      = true
     * endsWith(null, "def")     = false
     * endsWith("abcdef", null)  = false
     * endsWith("abcdef", "def") = true
     * endsWith("ABCDEF", "def") = false
     * endsWith("ABCDEF", "cde") = false
     * </pre>
     * 
     * @see java.lang.String#endsWith(String)
     * @param str
     *            the String to check, may be null
     * @param suffix
     *            the suffix to find, may be null
     * @return <code>true</code> if the String ends with the suffix, case
     *         sensitive, or both <code>null</code>
     * @since 2.4
     */
    public static boolean endsWith(String str, String suffix) {
        return endsWith(str, suffix, false);
    }

    /**
     * <p>
     * Case insensitive check if a String ends with a specified suffix.
     * </p>
     * 
     * <p>
     * <code>null</code>s are handled without exceptions. Two <code>null</code>
     * references are considered to be equal. The comparison is case
     * insensitive.
     * </p>
     * 
     * <pre>
     * endsWithIgnoreCase(null, null)      = true
     * endsWithIgnoreCase(null, "def")     = false
     * endsWithIgnoreCase("abcdef", null)  = false
     * endsWithIgnoreCase("abcdef", "def") = true
     * endsWithIgnoreCase("ABCDEF", "def") = true
     * endsWithIgnoreCase("ABCDEF", "cde") = false
     * </pre>
     * 
     * @see java.lang.String#endsWith(String)
     * @param str
     *            the String to check, may be null
     * @param suffix
     *            the suffix to find, may be null
     * @return <code>true</code> if the String ends with the suffix, case
     *         insensitive, or both <code>null</code>
     * @since 2.4
     */
    public static boolean endsWithIgnoreCase(String str, String suffix) {
        return endsWith(str, suffix, true);
    }

    /**
     * <p>
     * Check if a String ends with a specified suffix (optionally case
     * insensitive).
     * </p>
     * 
     * @see java.lang.String#endsWith(String)
     * @param str
     *            the String to check, may be null
     * @param suffix
     *            the suffix to find, may be null
     * @param ignoreCase
     *            inidicates whether the compare should ignore case (case
     *            insensitive) or not.
     * @return <code>true</code> if the String starts with the prefix or both
     *         <code>null</code>
     */
    private static boolean endsWith(String str, String suffix,
            boolean ignoreCase) {
        if (str == null || suffix == null) {
            return str == null && suffix == null;
        }
        if (suffix.length() > str.length()) {
            return false;
        }
        int strOffset = str.length() - suffix.length();
        return str.regionMatches(ignoreCase, strOffset, suffix, 0,
                suffix.length());
    }

    // ///////////////////////////////////////////
    //
    // ArrayUtils
    //
    // ///////////////////////////////////////////

    /**
     * An empty immutable <code>Object</code> array.
     */
    public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
    /**
     * An empty immutable <code>Class</code> array.
     */
    public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
    /**
     * An empty immutable <code>String</code> array.
     */
    public static final String[] EMPTY_STRING_ARRAY = new String[0];
    /**
     * An empty immutable <code>long</code> array.
     */
    public static final long[] EMPTY_LONG_ARRAY = new long[0];
    /**
     * An empty immutable <code>Long</code> array.
     */
    public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0];
    /**
     * An empty immutable <code>int</code> array.
     */
    public static final int[] EMPTY_INT_ARRAY = new int[0];
    /**
     * An empty immutable <code>Integer</code> array.
     */
    public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0];
    /**
     * An empty immutable <code>short</code> array.
     */
    public static final short[] EMPTY_SHORT_ARRAY = new short[0];
    /**
     * An empty immutable <code>Short</code> array.
     */
    public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0];
    /**
     * An empty immutable <code>byte</code> array.
     */
    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
    /**
     * An empty immutable <code>Byte</code> array.
     */
    public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0];
    /**
     * An empty immutable <code>double</code> array.
     */
    public static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
    /**
     * An empty immutable <code>Double</code> array.
     */
    public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0];
    /**
     * An empty immutable <code>float</code> array.
     */
    public static final float[] EMPTY_FLOAT_ARRAY = new float[0];
    /**
     * An empty immutable <code>Float</code> array.
     */
    public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0];
    /**
     * An empty immutable <code>boolean</code> array.
     */
    public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
    /**
     * An empty immutable <code>Boolean</code> array.
     */
    public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0];
    /**
     * An empty immutable <code>char</code> array.
     */
    public static final char[] EMPTY_CHAR_ARRAY = new char[0];
    /**
     * An empty immutable <code>Character</code> array.
     */
    public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0];

    /**
     * <p>
     * Outputs an array as a String, treating <code>null</code> as an empty
     * array.
     * </p>
     * 
     * <p>
     * Multi-dimensional arrays are handled correctly, including
     * multi-dimensional primitive arrays.
     * </p>
     * 
     * <p>
     * The format is that of Java source code, for example <code>{a,b}</code>.
     * </p>
     * 
     * @param array
     *            the array to get a toString for, may be <code>null</code>
     * @return a String representation of the array, '{}' if null array input
     */
    public static String toString(Object array) {
        return toString(array, "{}");
    }

    /**
     * <p>
     * Outputs an array as a String handling <code>null</code>s.
     * </p>
     * 
     * <p>
     * Multi-dimensional arrays are handled correctly, including
     * multi-dimensional primitive arrays.
     * </p>
     * 
     * <p>
     * The format is that of Java source code, for example <code>{a,b}</code>.
     * </p>
     * 
     * @param array
     *            the array to get a toString for, may be <code>null</code>
     * @param stringIfNull
     *            the String to return if the array is <code>null</code>
     * @return a String representation of the array
     */
    public static String toString(Object array, String stringIfNull) {
        if (array == null) {
            return stringIfNull;
        }
        return new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(
                array).toString();
    }

    /**
     * <p>
     * Compares two arrays, using equals(), handling multi-dimensional arrays
     * correctly.
     * </p>
     * 
     * <p>
     * Multi-dimensional primitive arrays are also handled correctly by this
     * method.
     * </p>
     * 
     * @param array1
     *            the left hand array to compare, may be <code>null</code>
     * @param array2
     *            the right hand array to compare, may be <code>null</code>
     * @return <code>true</code> if the arrays are equal
     */
    public static boolean isEquals(Object array1, Object array2) {
        return new EqualsBuilder().append(array1, array2).isEquals();
    }

    /**
     * <p>
     * Converts the given array into a {@link java.util.Map}. Each element of
     * the array must be either a {@link java.util.Map.Entry} or an Array,
     * containing at least two elements, where the first element is used as key
     * and the second as value.
     * </p>
     * 
     * <p>
     * This method can be used to initialize:
     * </p>
     * 
     * <pre>
     * // Create a Map mapping colors.
     * Map colorMap = MapUtils.toMap(new String[][] {{
     *     {"RED", "#FF0000"},
     *     {"GREEN", "#00FF00"},
     *     {"BLUE", "#0000FF"}});
     * </pre>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            an array whose elements are either a
     *            {@link java.util.Map.Entry} or an Array containing at least
     *            two elements, may be <code>null</code>
     * @return a <code>Map</code> that was created from the array
     * @throws IllegalArgumentException
     *             if one element of this Array is itself an Array containing
     *             less then two elements
     * @throws IllegalArgumentException
     *             if the array contains elements other than
     *             {@link java.util.Map.Entry} and an Array
     */
    public static Map<Object, Object> toMap(Object[] array) {
        if (array == null) {
            return null;
        }
        final Map<Object, Object> map = new HashMap<Object, Object>(
                (int) (array.length * 1.5));
        for (int i = 0; i < array.length; i++) {
            Object object = array[i];
            if (object instanceof Map.Entry<?, ?>) {
                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
                map.put(entry.getKey(), entry.getValue());
            } else if (object instanceof Object[]) {
                Object[] entry = (Object[]) object;
                if (entry.length < 2) {
                    throw new IllegalArgumentException("Array element " + i
                            + ", '" + object + "', has a length less than 2");
                }
                map.put(entry[0], entry[1]);
            } else {
                throw new IllegalArgumentException("Array element " + i + ", '"
                        + object
                        + "', is neither of type Map.Entry nor an Array");
            }
        }
        return map;
    }

    // Generic array
    // -----------------------------------------------------------------------
    /**
     * Create a type-safe generic array.
     * 
     * <p>
     * Arrays are covariant i.e. they cannot be created from a generic type:
     * </p>
     * 
     * <pre>
     * public static &lt;T&gt; T[] createAnArray(int size) {
     *     return T[size]; // compiler error here
     * }
     * 
     * public static &lt;T&gt; T[] createAnArray(int size) {
     *     return (T[]) Object[size]; // ClassCastException at runtime
     * }
     * </pre>
     * 
     * <p>
     * Therefore new arrays of generic types can be created with this method,
     * e.g. an arrays of Strings:
     * </p>
     * 
     * <pre>
     * String[] array = toArray(&quot;1&quot;, &quot;2&quot;);
     * String[] emptyArray = &lt;String&gt; toArray();
     * </pre>
     * 
     * The method is typically used in scenarios, where the caller itself uses
     * generic types that have to be combined into an array.
     * 
     * Note, this method makes only sense to provide arguments of the same type
     * so that the compiler can deduce the type of the array itself. While it is
     * possible to select the type explicitly like in
     * <code>Number[] array = <Number>toArray(new
     * Integer(42), new Double(Math.PI))</code>, there is no real advantage to
     * <code>new
     * Number[] {new Integer(42), new Double(Math.PI)}</code> anymore.
     * 
     * @param <T>
     *            the array's element type
     * @param items
     *            the items of the array
     * @return the array
     * @since 3.0
     */
    public static <T> T[] toArray(final T... items) {
        return items;
    }

    // Clone
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Shallow clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * The objects in the array are not cloned, thus there is no special
     * handling for multi-dimensional arrays.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to shallow clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static <T> T[] clone(T[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static long[] clone(long[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static int[] clone(int[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static short[] clone(short[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static char[] clone(char[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static byte[] clone(byte[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static double[] clone(double[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static float[] clone(float[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    /**
     * <p>
     * Clones an array returning a typecast result and handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            the array to clone, may be <code>null</code>
     * @return the cloned array, <code>null</code> if <code>null</code> input
     */
    public static boolean[] clone(boolean[] array) {
        if (array == null) {
            return null;
        }
        return array.clone();
    }

    // nullToEmpty
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Object[] nullToEmpty(Object[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static String[] nullToEmpty(String[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_STRING_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static long[] nullToEmpty(long[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_LONG_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static int[] nullToEmpty(int[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_INT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static short[] nullToEmpty(short[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_SHORT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static char[] nullToEmpty(char[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_CHAR_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static byte[] nullToEmpty(byte[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_BYTE_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static double[] nullToEmpty(double[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_DOUBLE_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static float[] nullToEmpty(float[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_FLOAT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static boolean[] nullToEmpty(boolean[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_BOOLEAN_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Long[] nullToEmpty(Long[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_LONG_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Integer[] nullToEmpty(Integer[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_INTEGER_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Short[] nullToEmpty(Short[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_SHORT_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Character[] nullToEmpty(Character[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_CHARACTER_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Byte[] nullToEmpty(Byte[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_BYTE_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Double[] nullToEmpty(Double[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_DOUBLE_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Float[] nullToEmpty(Float[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_FLOAT_OBJECT_ARRAY;
        }
        return array;
    }

    /**
     * <p>
     * Defensive programming technique to change a <code>null</code> reference
     * to an empty one.
     * </p>
     * 
     * <p>
     * This method returns an empty array for a <code>null</code> input array.
     * </p>
     * 
     * <p>
     * As a memory optimizing technique an empty array passed in will be
     * overridden with the empty <code>public static</code> references in this
     * class.
     * </p>
     * 
     * @param array
     *            the array to check for <code>null</code> or empty
     * @return the same array, <code>public static</code> empty array if
     *         <code>null</code> or empty input
     * @since 2.5
     */
    public static Boolean[] nullToEmpty(Boolean[] array) {
        if (array == null || array.length == 0) {
            return EMPTY_BOOLEAN_OBJECT_ARRAY;
        }
        return array;
    }

    // Subarrays
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Produces a new array containing the elements between the start and end
     * indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * <p>
     * The component type of the subarray is always the same as that of the
     * input array. Thus, if the input is an array of type <code>Date</code>,
     * the following usage is envisaged:
     * </p>
     * 
     * <pre>
     * Date[] someDates = (Date[]) subarray(allDates, 2, 5);
     * </pre>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static <T> T[] subarray(T[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        Class<?> type = array.getClass().getComponentType();
        if (newSize <= 0) {
            @SuppressWarnings("unchecked")
            // OK, because array is of type T
            final T[] emptyArray = (T[]) Array.newInstance(type, 0);
            return emptyArray;
        }
        @SuppressWarnings("unchecked")
        // OK, because array is of type T
        T[] subarray = (T[]) Array.newInstance(type, newSize);
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>long</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static long[] subarray(long[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_LONG_ARRAY;
        }

        long[] subarray = new long[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>int</code> array containing the elements between the
     * start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static int[] subarray(int[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_INT_ARRAY;
        }

        int[] subarray = new int[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>short</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static short[] subarray(short[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_SHORT_ARRAY;
        }

        short[] subarray = new short[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>char</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static char[] subarray(char[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_CHAR_ARRAY;
        }

        char[] subarray = new char[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>byte</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static byte[] subarray(byte[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_BYTE_ARRAY;
        }

        byte[] subarray = new byte[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>double</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static double[] subarray(double[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_DOUBLE_ARRAY;
        }

        double[] subarray = new double[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>float</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static float[] subarray(float[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_FLOAT_ARRAY;
        }

        float[] subarray = new float[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    /**
     * <p>
     * Produces a new <code>boolean</code> array containing the elements between
     * the start and end indices.
     * </p>
     * 
     * <p>
     * The start index is inclusive, the end index exclusive. Null array input
     * produces null output.
     * </p>
     * 
     * @param array
     *            the array
     * @param startIndexInclusive
     *            the starting index. Undervalue (&lt;0) is promoted to 0,
     *            overvalue (&gt;array.length) results in an empty array.
     * @param endIndexExclusive
     *            elements up to endIndex-1 are present in the returned
     *            subarray. Undervalue (&lt; startIndex) produces empty array,
     *            overvalue (&gt;array.length) is demoted to array length.
     * @return a new array containing the elements between the start and end
     *         indices.
     * @since 2.1
     */
    public static boolean[] subarray(boolean[] array, int startIndexInclusive,
            int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        int newSize = endIndexExclusive - startIndexInclusive;
        if (newSize <= 0) {
            return EMPTY_BOOLEAN_ARRAY;
        }

        boolean[] subarray = new boolean[newSize];
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

    // Is same length
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * 
     * <p>
     * Any multi-dimensional aspects of the arrays are ignored.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(Object[] array1, Object[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(long[] array1, long[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(int[] array1, int[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(short[] array1, short[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(char[] array1, char[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(byte[] array1, byte[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(double[] array1, double[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(float[] array1, float[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Checks whether two arrays are the same length, treating <code>null</code>
     * arrays as length <code>0</code>.
     * </p>
     * 
     * @param array1
     *            the first array, may be <code>null</code>
     * @param array2
     *            the second array, may be <code>null</code>
     * @return <code>true</code> if length of arrays matches, treating
     *         <code>null</code> as an empty array
     */
    public static boolean isSameLength(boolean[] array1, boolean[] array2) {
        if ((array1 == null && array2 != null && array2.length > 0)
                || (array2 == null && array1 != null && array1.length > 0)
                || (array1 != null && array2 != null && array1.length != array2.length)) {
            return false;
        }
        return true;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Returns the length of the specified array. This method can deal with
     * <code>Object</code> arrays and with primitive arrays.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, <code>0</code> is returned.
     * </p>
     * 
     * <pre>
     * getLength(null)            = 0
     * getLength([])              = 0
     * getLength([null])          = 1
     * getLength([true, false])   = 2
     * getLength([1, 2, 3])       = 3
     * getLength(["a", "b", "c"]) = 3
     * </pre>
     * 
     * @param array
     *            the array to retrieve the length from, may be null
     * @return The length of the array, or <code>0</code> if the array is
     *         <code>null</code>
     * @throws IllegalArgumentException
     *             if the object arguement is not an array.
     * @since 2.1
     */
    public static int getLength(Object array) {
        if (array == null) {
            return 0;
        }
        return Array.getLength(array);
    }

    /**
     * <p>
     * Checks whether two arrays are the same type taking into account
     * multi-dimensional arrays.
     * </p>
     * 
     * @param array1
     *            the first array, must not be <code>null</code>
     * @param array2
     *            the second array, must not be <code>null</code>
     * @return <code>true</code> if type of arrays matches
     * @throws IllegalArgumentException
     *             if either array is <code>null</code>
     */
    public static boolean isSameType(Object array1, Object array2) {
        if (array1 == null || array2 == null) {
            throw new IllegalArgumentException("The Array must not be null");
        }
        return array1.getClass().getName().equals(array2.getClass().getName());
    }

    // Reverse
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * There is no special handling for multi-dimensional arrays.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(Object[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        Object tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(long[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        long tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(int[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        int tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(short[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        short tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(char[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        char tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(byte[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        byte tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(double[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        double tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(float[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        float tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    /**
     * <p>
     * Reverses the order of the given array.
     * </p>
     * 
     * <p>
     * This method does nothing for a <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to reverse, may be <code>null</code>
     */
    public static void reverse(boolean[] array) {
        if (array == null) {
            return;
        }
        int i = 0;
        int j = array.length - 1;
        boolean tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

    // IndexOf search
    // ----------------------------------------------------------------------

    // Object IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given object in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param objectToFind
     *            the object to find, may be <code>null</code>
     * @return the index of the object within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int indexOf(Object[] array, Object objectToFind) {
        return indexOf(array, objectToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given object in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param objectToFind
     *            the object to find, may be <code>null</code>
     * @param startIndex
     *            the index to start searching at
     * @return the index of the object within the array starting at the index,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int indexOf(Object[] array, Object objectToFind,
            int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        if (objectToFind == null) {
            for (int i = startIndex; i < array.length; i++) {
                if (array[i] == null) {
                    return i;
                }
            }
        } else if (array.getClass().getComponentType().isInstance(objectToFind)) {
            for (int i = startIndex; i < array.length; i++) {
                if (objectToFind.equals(array[i])) {
                    return i;
                }
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given object within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param objectToFind
     *            the object to find, may be <code>null</code>
     * @return the last index of the object within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(Object[] array, Object objectToFind) {
        return lastIndexOf(array, objectToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given object in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param objectToFind
     *            the object to find, may be <code>null</code>
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the object within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(Object[] array, Object objectToFind,
            int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        if (objectToFind == null) {
            for (int i = startIndex; i >= 0; i--) {
                if (array[i] == null) {
                    return i;
                }
            }
        } else if (array.getClass().getComponentType().isInstance(objectToFind)) {
            for (int i = startIndex; i >= 0; i--) {
                if (objectToFind.equals(array[i])) {
                    return i;
                }
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the object is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param objectToFind
     *            the object to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(Object[] array, Object objectToFind) {
        return indexOf(array, objectToFind) != INDEX_NOT_FOUND;
    }

    // long IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(long[] array, long valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(long[] array, long valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(long[] array, long valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(long[] array, long valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(long[] array, long valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // int IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(int[] array, int valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(int[] array, int valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(int[] array, int valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(int[] array, int valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(int[] array, int valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // short IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(short[] array, short valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(short[] array, short valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(short[] array, short valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(short[] array, short valueToFind,
            int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(short[] array, short valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // char IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     * @since 2.1
     */
    public static int indexOf(char[] array, char valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     * @since 2.1
     */
    public static int indexOf(char[] array, char valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     * @since 2.1
     */
    public static int lastIndexOf(char[] array, char valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     * @since 2.1
     */
    public static int lastIndexOf(char[] array, char valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     * @since 2.1
     */
    public static boolean contains(char[] array, char valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // byte IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(byte[] array, byte valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(byte[] array, byte valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(byte[] array, byte valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(byte[] array, byte valueToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(byte[] array, byte valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // double IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(double[] array, double valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value within a given tolerance in the array.
     * This method will return the index of the first value which falls between
     * the region defined by valueToFind - tolerance and valueToFind +
     * tolerance.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param tolerance
     *            tolerance of the search
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(double[] array, double valueToFind,
            double tolerance) {
        return indexOf(array, valueToFind, 0, tolerance);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(double[] array, double valueToFind, int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index. This method will return the index of the first value which falls
     * between the region defined by valueToFind - tolerance and valueToFind +
     * tolerance.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @param tolerance
     *            tolerance of the search
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(double[] array, double valueToFind,
            int startIndex, double tolerance) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        double min = valueToFind - tolerance;
        double max = valueToFind + tolerance;
        for (int i = startIndex; i < array.length; i++) {
            if (array[i] >= min && array[i] <= max) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(double[] array, double valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value within a given tolerance in the
     * array. This method will return the index of the last value which falls
     * between the region defined by valueToFind - tolerance and valueToFind +
     * tolerance.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param tolerance
     *            tolerance of the search
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int lastIndexOf(double[] array, double valueToFind,
            double tolerance) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(double[] array, double valueToFind,
            int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index. This method will return the index of the last value which
     * falls between the region defined by valueToFind - tolerance and
     * valueToFind + tolerance.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @param tolerance
     *            search for value within plus/minus this amount
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(double[] array, double valueToFind,
            int startIndex, double tolerance) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        double min = valueToFind - tolerance;
        double max = valueToFind + tolerance;
        for (int i = startIndex; i >= 0; i--) {
            if (array[i] >= min && array[i] <= max) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(double[] array, double valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if a value falling within the given tolerance is in the given
     * array. If the array contains a value within the inclusive range defined
     * by (value - tolerance) to (value + tolerance).
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search
     * @param valueToFind
     *            the value to find
     * @param tolerance
     *            the array contains the tolerance of the search
     * @return true if value falling within tolerance is in array
     */
    public static boolean contains(double[] array, double valueToFind,
            double tolerance) {
        return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND;
    }

    // float IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(float[] array, float valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(float[] array, float valueToFind, int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(float[] array, float valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(float[] array, float valueToFind,
            int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(float[] array, float valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // boolean IndexOf
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Finds the index of the given value in the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(boolean[] array, boolean valueToFind) {
        return indexOf(array, valueToFind, 0);
    }

    /**
     * <p>
     * Finds the index of the given value in the array starting at the given
     * index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex is treated as zero. A startIndex larger than the
     * array length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).
     * </p>
     * 
     * @param array
     *            the array to search through for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the index to start searching at
     * @return the index of the value within the array, {@link #INDEX_NOT_FOUND}
     *         (<code>-1</code>) if not found or <code>null</code> array input
     */
    public static int indexOf(boolean[] array, boolean valueToFind,
            int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        for (int i = startIndex; i < array.length; i++) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Finds the last index of the given value within the array.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) if
     * <code>null</code> array input.
     * </p>
     * 
     * @param array
     *            the array to travers backwords looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the object to find
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(boolean[] array, boolean valueToFind) {
        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Finds the last index of the given value in the array starting at the
     * given index.
     * </p>
     * 
     * <p>
     * This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a
     * <code>null</code> input array.
     * </p>
     * 
     * <p>
     * A negative startIndex will return {@link #INDEX_NOT_FOUND} (
     * <code>-1</code>). A startIndex larger than the array length will search
     * from the end of the array.
     * </p>
     * 
     * @param array
     *            the array to traverse for looking for the object, may be
     *            <code>null</code>
     * @param valueToFind
     *            the value to find
     * @param startIndex
     *            the start index to travers backwards from
     * @return the last index of the value within the array,
     *         {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or
     *         <code>null</code> array input
     */
    public static int lastIndexOf(boolean[] array, boolean valueToFind,
            int startIndex) {
        if (isEmpty(array)) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            return INDEX_NOT_FOUND;
        } else if (startIndex >= array.length) {
            startIndex = array.length - 1;
        }
        for (int i = startIndex; i >= 0; i--) {
            if (valueToFind == array[i]) {
                return i;
            }
        }
        return INDEX_NOT_FOUND;
    }

    /**
     * <p>
     * Checks if the value is in the given array.
     * </p>
     * 
     * <p>
     * The method returns <code>false</code> if a <code>null</code> array is
     * passed in.
     * </p>
     * 
     * @param array
     *            the array to search through
     * @param valueToFind
     *            the value to find
     * @return <code>true</code> if the array contains the object
     */
    public static boolean contains(boolean[] array, boolean valueToFind) {
        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
    }

    // Primitive/Object array converters
    // ----------------------------------------------------------------------

    // Character array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Characters to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Character</code> array, may be <code>null</code>
     * @return a <code>char</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static char[] toPrimitive(Character[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_CHAR_ARRAY;
        }
        final char[] result = new char[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].charValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Character to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Character</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>char</code> array, <code>null</code> if null array input
     */
    public static char[] toPrimitive(Character[] array, char valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_CHAR_ARRAY;
        }
        final char[] result = new char[array.length];
        for (int i = 0; i < array.length; i++) {
            Character b = array[i];
            result[i] = (b == null ? valueForNull : b.charValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive chars to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>char</code> array
     * @return a <code>Character</code> array, <code>null</code> if null array
     *         input
     */
    public static Character[] toObject(char[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_CHARACTER_OBJECT_ARRAY;
        }
        final Character[] result = new Character[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Character.valueOf(array[i]);
        }
        return result;
    }

    // Long array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Longs to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Long</code> array, may be <code>null</code>
     * @return a <code>long</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static long[] toPrimitive(Long[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_LONG_ARRAY;
        }
        final long[] result = new long[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].longValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Long to primitives handling <code>null</code>
     * .
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Long</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>long</code> array, <code>null</code> if null array input
     */
    public static long[] toPrimitive(Long[] array, long valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_LONG_ARRAY;
        }
        final long[] result = new long[array.length];
        for (int i = 0; i < array.length; i++) {
            Long b = array[i];
            result[i] = (b == null ? valueForNull : b.longValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive longs to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>long</code> array
     * @return a <code>Long</code> array, <code>null</code> if null array input
     */
    public static Long[] toObject(long[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_LONG_OBJECT_ARRAY;
        }
        final Long[] result = new Long[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Long.valueOf(array[i]);
        }
        return result;
    }

    // Int array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Integers to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Integer</code> array, may be <code>null</code>
     * @return an <code>int</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static int[] toPrimitive(Integer[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_INT_ARRAY;
        }
        final int[] result = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].intValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Integer to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Integer</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return an <code>int</code> array, <code>null</code> if null array input
     */
    public static int[] toPrimitive(Integer[] array, int valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_INT_ARRAY;
        }
        final int[] result = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            Integer b = array[i];
            result[i] = (b == null ? valueForNull : b.intValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive ints to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            an <code>int</code> array
     * @return an <code>Integer</code> array, <code>null</code> if null array
     *         input
     */
    public static Integer[] toObject(int[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_INTEGER_OBJECT_ARRAY;
        }
        final Integer[] result = new Integer[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Integer.valueOf(array[i]);
        }
        return result;
    }

    // Short array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Shorts to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Short</code> array, may be <code>null</code>
     * @return a <code>byte</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static short[] toPrimitive(Short[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_SHORT_ARRAY;
        }
        final short[] result = new short[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].shortValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Short to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Short</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>byte</code> array, <code>null</code> if null array input
     */
    public static short[] toPrimitive(Short[] array, short valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_SHORT_ARRAY;
        }
        final short[] result = new short[array.length];
        for (int i = 0; i < array.length; i++) {
            Short b = array[i];
            result[i] = (b == null ? valueForNull : b.shortValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive shorts to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>short</code> array
     * @return a <code>Short</code> array, <code>null</code> if null array input
     */
    public static Short[] toObject(short[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_SHORT_OBJECT_ARRAY;
        }
        final Short[] result = new Short[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Short.valueOf(array[i]);
        }
        return result;
    }

    // Byte array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Bytes to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Byte</code> array, may be <code>null</code>
     * @return a <code>byte</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static byte[] toPrimitive(Byte[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BYTE_ARRAY;
        }
        final byte[] result = new byte[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].byteValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Bytes to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Byte</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>byte</code> array, <code>null</code> if null array input
     */
    public static byte[] toPrimitive(Byte[] array, byte valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BYTE_ARRAY;
        }
        final byte[] result = new byte[array.length];
        for (int i = 0; i < array.length; i++) {
            Byte b = array[i];
            result[i] = (b == null ? valueForNull : b.byteValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive bytes to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>byte</code> array
     * @return a <code>Byte</code> array, <code>null</code> if null array input
     */
    public static Byte[] toObject(byte[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BYTE_OBJECT_ARRAY;
        }
        final Byte[] result = new Byte[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Byte.valueOf(array[i]);
        }
        return result;
    }

    // Double array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Doubles to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Double</code> array, may be <code>null</code>
     * @return a <code>double</code> array, <code>null</code> if null array
     *         input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static double[] toPrimitive(Double[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_DOUBLE_ARRAY;
        }
        final double[] result = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].doubleValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Doubles to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Double</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>double</code> array, <code>null</code> if null array
     *         input
     */
    public static double[] toPrimitive(Double[] array, double valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_DOUBLE_ARRAY;
        }
        final double[] result = new double[array.length];
        for (int i = 0; i < array.length; i++) {
            Double b = array[i];
            result[i] = (b == null ? valueForNull : b.doubleValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive doubles to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>double</code> array
     * @return a <code>Double</code> array, <code>null</code> if null array
     *         input
     */
    public static Double[] toObject(double[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_DOUBLE_OBJECT_ARRAY;
        }
        final Double[] result = new Double[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Double.valueOf(array[i]);
        }
        return result;
    }

    // Float array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Floats to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Float</code> array, may be <code>null</code>
     * @return a <code>float</code> array, <code>null</code> if null array input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static float[] toPrimitive(Float[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_FLOAT_ARRAY;
        }
        final float[] result = new float[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].floatValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Floats to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Float</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>float</code> array, <code>null</code> if null array input
     */
    public static float[] toPrimitive(Float[] array, float valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_FLOAT_ARRAY;
        }
        final float[] result = new float[array.length];
        for (int i = 0; i < array.length; i++) {
            Float b = array[i];
            result[i] = (b == null ? valueForNull : b.floatValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive floats to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>float</code> array
     * @return a <code>Float</code> array, <code>null</code> if null array input
     */
    public static Float[] toObject(float[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_FLOAT_OBJECT_ARRAY;
        }
        final Float[] result = new Float[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = Float.valueOf(array[i]);
        }
        return result;
    }

    // Boolean array converters
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Converts an array of object Booleans to primitives.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Boolean</code> array, may be <code>null</code>
     * @return a <code>boolean</code> array, <code>null</code> if null array
     *         input
     * @throws NullPointerException
     *             if array content is <code>null</code>
     */
    public static boolean[] toPrimitive(Boolean[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BOOLEAN_ARRAY;
        }
        final boolean[] result = new boolean[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = array[i].booleanValue();
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of object Booleans to primitives handling
     * <code>null</code>.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>Boolean</code> array, may be <code>null</code>
     * @param valueForNull
     *            the value to insert if <code>null</code> found
     * @return a <code>boolean</code> array, <code>null</code> if null array
     *         input
     */
    public static boolean[] toPrimitive(Boolean[] array, boolean valueForNull) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BOOLEAN_ARRAY;
        }
        final boolean[] result = new boolean[array.length];
        for (int i = 0; i < array.length; i++) {
            Boolean b = array[i];
            result[i] = (b == null ? valueForNull : b.booleanValue());
        }
        return result;
    }

    /**
     * <p>
     * Converts an array of primitive booleans to objects.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            a <code>boolean</code> array
     * @return a <code>Boolean</code> array, <code>null</code> if null array
     *         input
     */
    public static Boolean[] toObject(boolean[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_BOOLEAN_OBJECT_ARRAY;
        }
        final Boolean[] result = new Boolean[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE);
        }
        return result;
    }

    // ----------------------------------------------------------------------
    /**
     * <p>
     * Checks if an array of Objects is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static <T> boolean isEmpty(T[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive longs is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(long[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive ints is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(int[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive shorts is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(short[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive chars is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(char[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive bytes is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(byte[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive doubles is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(double[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive floats is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(float[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    /**
     * <p>
     * Checks if an array of primitive booleans is empty or <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(boolean[] array) {
        if (array == null || array.length == 0) {
            return true;
        }
        return false;
    }

    // ----------------------------------------------------------------------
    /**
     * <p>
     * Checks if an array of Objects is not empty or not <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static <T> boolean isNotEmpty(T[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive longs is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(long[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive ints is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(int[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive shorts is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(short[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive chars is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(char[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive bytes is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(byte[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive doubles is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(double[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive floats is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(float[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Checks if an array of primitive booleans is not empty or not
     * <code>null</code>.
     * </p>
     * 
     * @param array
     *            the array to test
     * @return <code>true</code> if the array is not empty or not
     *         <code>null</code>
     * @since 2.5
     */
    public static boolean isNotEmpty(boolean[] array) {
        return (array != null && array.length != 0);
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(null, null)     = null
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * addAll([null], [null]) = [null, null]
     * addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array, may
     *            be <code>null</code>
     * @param array2
     *            the second array whose elements are added to the new array,
     *            may be <code>null</code>
     * @return The new array, <code>null</code> if both arrays are
     *         <code>null</code>. The type of the new array is the type of the
     *         first array, unless the first array is null, in which case the
     *         type is the same as the second array.
     * @since 2.1
     * @throws IllegalArgumentException
     *             if the array types are incompatible
     */
    public static <T> T[] addAll(T[] array1, T... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        final Class<?> type1 = array1.getClass().getComponentType();
        @SuppressWarnings("unchecked")
        // OK, because array is of type T
        T[] joinedArray = (T[]) Array.newInstance(type1, array1.length
                + array2.length);
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        try {
            System.arraycopy(array2, 0, joinedArray, array1.length,
                    array2.length);
        } catch (ArrayStoreException ase) {
            // Check if problem was due to incompatible types
            /*
             * We do this here, rather than before the copy because: - it would
             * be a wasted check most of the time - safer, in case check turns
             * out to be too strict
             */
            final Class<?> type2 = array2.getClass().getComponentType();
            if (!type1.isAssignableFrom(type2)) {
                throw new IllegalArgumentException("Cannot store "
                        + type2.getName() + " in an array of "
                        + type1.getName(), ase);
            }
            throw ase; // No, so rethrow original
        }
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new boolean[] array.
     * @since 2.1
     */
    public static boolean[] addAll(boolean[] array1, boolean... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        boolean[] joinedArray = new boolean[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new char[] array.
     * @since 2.1
     */
    public static char[] addAll(char[] array1, char... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        char[] joinedArray = new char[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new byte[] array.
     * @since 2.1
     */
    public static byte[] addAll(byte[] array1, byte... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        byte[] joinedArray = new byte[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new short[] array.
     * @since 2.1
     */
    public static short[] addAll(short[] array1, short... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        short[] joinedArray = new short[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new int[] array.
     * @since 2.1
     */
    public static int[] addAll(int[] array1, int... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        int[] joinedArray = new int[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new long[] array.
     * @since 2.1
     */
    public static long[] addAll(long[] array1, long... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        long[] joinedArray = new long[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new float[] array.
     * @since 2.1
     */
    public static float[] addAll(float[] array1, float... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        float[] joinedArray = new float[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Adds all the elements of the given arrays into a new array.
     * </p>
     * <p>
     * The new array contains all of the element of <code>array1</code> followed
     * by all of the elements <code>array2</code>. When an array is returned, it
     * is always a new array.
     * </p>
     * 
     * <pre>
     * addAll(array1, null)   = cloned copy of array1
     * addAll(null, array2)   = cloned copy of array2
     * addAll([], [])         = []
     * </pre>
     * 
     * @param array1
     *            the first array whose elements are added to the new array.
     * @param array2
     *            the second array whose elements are added to the new array.
     * @return The new double[] array.
     * @since 2.1
     */
    public static double[] addAll(double[] array1, double... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        double[] joinedArray = new double[array1.length + array2.length];
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        return joinedArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element, unless the
     * element itself is null, in which case the return type is Object[]
     * </p>
     * 
     * <pre>
     * add(null, null)      = [null]
     * add(null, "a")       = ["a"]
     * add(["a"], null)     = ["a", null]
     * add(["a"], "b")      = ["a", "b"]
     * add(["a", "b"], "c") = ["a", "b", "c"]
     * </pre>
     * 
     * @param array
     *            the array to "add" the element to, may be <code>null</code>
     * @param element
     *            the object to add, may be <code>null</code>
     * @return A new array containing the existing elements plus the new element
     *         The returned array type will be that of the input array (unless
     *         null), in which case it will have the same type as the element.
     *         If both are null, an IllegalArgumentException is thrown
     * @since 2.1
     * @throws IllegalArgumentException
     *             if both arguments are null
     */
    public static <T> T[] add(T[] array, T element) {
        Class<?> type;
        if (array != null) {
            type = array.getClass();
        } else if (element != null) {
            type = element.getClass();
        } else {
            throw new IllegalArgumentException("Arguments cannot both be null");
        }
        @SuppressWarnings("unchecked")
        // type must be T
        T[] newArray = (T[]) copyArrayGrow1(array, type);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, true)          = [true]
     * add([true], false)       = [true, false]
     * add([true, false], true) = [true, false, true]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static boolean[] add(boolean[] array, boolean element) {
        boolean[] newArray = (boolean[]) copyArrayGrow1(array, Boolean.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static byte[] add(byte[] array, byte element) {
        byte[] newArray = (byte[]) copyArrayGrow1(array, Byte.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, '0')       = ['0']
     * add(['1'], '0')      = ['1', '0']
     * add(['1', '0'], '1') = ['1', '0', '1']
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static char[] add(char[] array, char element) {
        char[] newArray = (char[]) copyArrayGrow1(array, Character.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static double[] add(double[] array, double element) {
        double[] newArray = (double[]) copyArrayGrow1(array, Double.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static float[] add(float[] array, float element) {
        float[] newArray = (float[]) copyArrayGrow1(array, Float.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static int[] add(int[] array, int element) {
        int[] newArray = (int[]) copyArrayGrow1(array, Integer.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static long[] add(long[] array, long element) {
        long[] newArray = (long[]) copyArrayGrow1(array, Long.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * <p>
     * Copies the given array and adds the given element at the end of the new
     * array.
     * </p>
     * 
     * <p>
     * The new array contains the same elements of the input array plus the
     * given element in the last position. The component type of the new array
     * is the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0)   = [0]
     * add([1], 0)    = [1, 0]
     * add([1, 0], 1) = [1, 0, 1]
     * </pre>
     * 
     * @param array
     *            the array to copy and add the element to, may be
     *            <code>null</code>
     * @param element
     *            the object to add at the last index of the new array
     * @return A new array containing the existing elements plus the new element
     * @since 2.1
     */
    public static short[] add(short[] array, short element) {
        short[] newArray = (short[]) copyArrayGrow1(array, Short.TYPE);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

    /**
     * Returns a copy of the given array of size 1 greater than the argument.
     * The last value of the array is left to the default value.
     * 
     * @param array
     *            The array to copy, must not be <code>null</code>.
     * @param newArrayComponentType
     *            If <code>array</code> is <code>null</code>, create a size 1
     *            array of this type.
     * @return A new copy of the array of size 1 greater than the input.
     */
    private static Object copyArrayGrow1(Object array,
            Class<?> newArrayComponentType) {
        if (array != null) {
            int arrayLength = Array.getLength(array);
            Object newArray = Array.newInstance(array.getClass()
                    .getComponentType(), arrayLength + 1);
            System.arraycopy(array, 0, newArray, 0, arrayLength);
            return newArray;
        }
        return Array.newInstance(newArrayComponentType, 1);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0, null)      = [null]
     * add(null, 0, "a")       = ["a"]
     * add(["a"], 1, null)     = ["a", null]
     * add(["a"], 1, "b")      = ["a", "b"]
     * add(["a", "b"], 3, "c") = ["a", "b", "c"]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     * @throws IllegalArgumentException
     *             if both array and element are null
     */
    public static <T> T[] add(T[] array, int index, T element) {
        Class<?> clss = null;
        if (array != null) {
            clss = array.getClass().getComponentType();
        } else if (element != null) {
            clss = element.getClass();
        } else {
            throw new IllegalArgumentException(
                    "Array and element cannot both be null");
        }
        @SuppressWarnings("unchecked")
        // the add method creates an array of type clss, which is type T
        final T[] newArray = (T[]) add(array, index, element, clss);
        return newArray;
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0, true)          = [true]
     * add([true], 0, false)       = [false, true]
     * add([false], 1, true)       = [false, true]
     * add([true, false], 1, true) = [true, true, false]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static boolean[] add(boolean[] array, int index, boolean element) {
        return (boolean[]) add(array, index, Boolean.valueOf(element),
                Boolean.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add(null, 0, 'a')            = ['a']
     * add(['a'], 0, 'b')           = ['b', 'a']
     * add(['a', 'b'], 0, 'c')      = ['c', 'a', 'b']
     * add(['a', 'b'], 1, 'k')      = ['a', 'k', 'b']
     * add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c']
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static char[] add(char[] array, int index, char element) {
        return (char[]) add(array, index, Character.valueOf(element),
                Character.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1], 0, 2)         = [2, 1]
     * add([2, 6], 2, 3)      = [2, 6, 3]
     * add([2, 6], 0, 1)      = [1, 2, 6]
     * add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static byte[] add(byte[] array, int index, byte element) {
        return (byte[]) add(array, index, Byte.valueOf(element), Byte.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1], 0, 2)         = [2, 1]
     * add([2, 6], 2, 10)     = [2, 6, 10]
     * add([2, 6], 0, -4)     = [-4, 2, 6]
     * add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static short[] add(short[] array, int index, short element) {
        return (short[]) add(array, index, Short.valueOf(element), Short.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1], 0, 2)         = [2, 1]
     * add([2, 6], 2, 10)     = [2, 6, 10]
     * add([2, 6], 0, -4)     = [-4, 2, 6]
     * add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static int[] add(int[] array, int index, int element) {
        return (int[]) add(array, index, Integer.valueOf(element), Integer.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1L], 0, 2L)           = [2L, 1L]
     * add([2L, 6L], 2, 10L)      = [2L, 6L, 10L]
     * add([2L, 6L], 0, -4L)      = [-4L, 2L, 6L]
     * add([2L, 6L, 3L], 2, 1L)   = [2L, 6L, 1L, 3L]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static long[] add(long[] array, int index, long element) {
        return (long[]) add(array, index, Long.valueOf(element), Long.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1.1f], 0, 2.2f)               = [2.2f, 1.1f]
     * add([2.3f, 6.4f], 2, 10.5f)        = [2.3f, 6.4f, 10.5f]
     * add([2.6f, 6.7f], 0, -4.8f)        = [-4.8f, 2.6f, 6.7f]
     * add([2.9f, 6.0f, 0.3f], 2, 1.0f)   = [2.9f, 6.0f, 1.0f, 0.3f]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static float[] add(float[] array, int index, float element) {
        return (float[]) add(array, index, Float.valueOf(element), Float.TYPE);
    }

    /**
     * <p>
     * Inserts the specified element at the specified position in the array.
     * Shifts the element currently at that position (if any) and any subsequent
     * elements to the right (adds one to their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * plus the given element on the specified position. The component type of
     * the returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, a new one element array is
     * returned whose component type is the same as the element.
     * </p>
     * 
     * <pre>
     * add([1.1], 0, 2.2)              = [2.2, 1.1]
     * add([2.3, 6.4], 2, 10.5)        = [2.3, 6.4, 10.5]
     * add([2.6, 6.7], 0, -4.8)        = [-4.8, 2.6, 6.7]
     * add([2.9, 6.0, 0.3], 2, 1.0)    = [2.9, 6.0, 1.0, 0.3]
     * </pre>
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @return A new array containing the existing elements and the new element
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >
     *             array.length).
     */
    public static double[] add(double[] array, int index, double element) {
        return (double[]) add(array, index, Double.valueOf(element),
                Double.TYPE);
    }

    /**
     * Underlying implementation of add(array, index, element) methods. The last
     * parameter is the class, which may not equal element.getClass for
     * primitives.
     * 
     * @param array
     *            the array to add the element to, may be <code>null</code>
     * @param index
     *            the position of the new object
     * @param element
     *            the object to add
     * @param clss
     *            the type of the element being added
     * @return A new array containing the existing elements and the new element
     */
    private static Object add(Object array, int index, Object element,
            Class<?> clss) {
        if (array == null) {
            if (index != 0) {
                throw new IndexOutOfBoundsException("Index: " + index
                        + ", Length: 0");
            }
            Object joinedArray = Array.newInstance(clss, 1);
            Array.set(joinedArray, 0, element);
            return joinedArray;
        }
        int length = Array.getLength(array);
        if (index > length || index < 0) {
            throw new IndexOutOfBoundsException("Index: " + index
                    + ", Length: " + length);
        }
        Object result = Array.newInstance(clss, length + 1);
        System.arraycopy(array, 0, result, 0, index);
        Array.set(result, index, element);
        if (index < length) {
            System.arraycopy(array, index, result, index + 1, length - index);
        }
        return result;
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove(["a"], 0)           = []
     * remove(["a", "b"], 0)      = ["b"]
     * remove(["a", "b"], 1)      = ["a"]
     * remove(["a", "b", "c"], 1) = ["a", "c"]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    @SuppressWarnings("unchecked")
    // remove() always creates an array of the same type as its input
    public static <T> T[] remove(T[] array, int index) {
        return (T[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, "a")            = null
     * removeElement([], "a")              = []
     * removeElement(["a"], "b")           = ["a"]
     * removeElement(["a", "b"], "a")      = ["b"]
     * removeElement(["a", "b", "a"], "a") = ["b", "a"]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static <T> T[] removeElement(T[] array, Object element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([true], 0)              = []
     * remove([true, false], 0)       = [false]
     * remove([true, false], 1)       = [true]
     * remove([true, true, false], 1) = [true, false]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static boolean[] remove(boolean[] array, int index) {
        return (boolean[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, true)                = null
     * removeElement([], true)                  = []
     * removeElement([true], false)             = [true]
     * removeElement([true, false], false)      = [true]
     * removeElement([true, false, true], true) = [false, true]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static boolean[] removeElement(boolean[] array, boolean element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1], 0)          = []
     * remove([1, 0], 0)       = [0]
     * remove([1, 0], 1)       = [1]
     * remove([1, 0, 1], 1)    = [1, 1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static byte[] remove(byte[] array, int index) {
        return (byte[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1)        = null
     * removeElement([], 1)          = []
     * removeElement([1], 0)         = [1]
     * removeElement([1, 0], 0)      = [1]
     * removeElement([1, 0, 1], 1)   = [0, 1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static byte[] removeElement(byte[] array, byte element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove(['a'], 0)           = []
     * remove(['a', 'b'], 0)      = ['b']
     * remove(['a', 'b'], 1)      = ['a']
     * remove(['a', 'b', 'c'], 1) = ['a', 'c']
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static char[] remove(char[] array, int index) {
        return (char[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 'a')            = null
     * removeElement([], 'a')              = []
     * removeElement(['a'], 'b')           = ['a']
     * removeElement(['a', 'b'], 'a')      = ['b']
     * removeElement(['a', 'b', 'a'], 'a') = ['b', 'a']
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static char[] removeElement(char[] array, char element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1.1], 0)           = []
     * remove([2.5, 6.0], 0)      = [6.0]
     * remove([2.5, 6.0], 1)      = [2.5]
     * remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static double[] remove(double[] array, int index) {
        return (double[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1.1)            = null
     * removeElement([], 1.1)              = []
     * removeElement([1.1], 1.2)           = [1.1]
     * removeElement([1.1, 2.3], 1.1)      = [2.3]
     * removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static double[] removeElement(double[] array, double element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1.1], 0)           = []
     * remove([2.5, 6.0], 0)      = [6.0]
     * remove([2.5, 6.0], 1)      = [2.5]
     * remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static float[] remove(float[] array, int index) {
        return (float[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1.1)            = null
     * removeElement([], 1.1)              = []
     * removeElement([1.1], 1.2)           = [1.1]
     * removeElement([1.1, 2.3], 1.1)      = [2.3]
     * removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static float[] removeElement(float[] array, float element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1], 0)         = []
     * remove([2, 6], 0)      = [6]
     * remove([2, 6], 1)      = [2]
     * remove([2, 6, 3], 1)   = [2, 3]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static int[] remove(int[] array, int index) {
        return (int[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1)      = null
     * removeElement([], 1)        = []
     * removeElement([1], 2)       = [1]
     * removeElement([1, 3], 1)    = [3]
     * removeElement([1, 3, 1], 1) = [3, 1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static int[] removeElement(int[] array, int element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1], 0)         = []
     * remove([2, 6], 0)      = [6]
     * remove([2, 6], 1)      = [2]
     * remove([2, 6, 3], 1)   = [2, 3]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static long[] remove(long[] array, int index) {
        return (long[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1)      = null
     * removeElement([], 1)        = []
     * removeElement([1], 2)       = [1]
     * removeElement([1, 3], 1)    = [3]
     * removeElement([1, 3, 1], 1) = [3, 1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static long[] removeElement(long[] array, long element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * <pre>
     * remove([1], 0)         = []
     * remove([2, 6], 0)      = [6]
     * remove([2, 6], 1)      = [2]
     * remove([2, 6, 3], 1)   = [2, 3]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    public static short[] remove(short[] array, int index) {
        return (short[]) remove((Object) array, index);
    }

    /**
     * <p>
     * Removes the first occurrence of the specified element from the specified
     * array. All subsequent elements are shifted to the left (substracts one
     * from their indices). If the array doesn't contains such an element, no
     * elements are removed from the array.
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the first occurrence of the specified element. The component type
     * of the returned array is always the same as that of the input array.
     * </p>
     * 
     * <pre>
     * removeElement(null, 1)      = null
     * removeElement([], 1)        = []
     * removeElement([1], 2)       = [1]
     * removeElement([1, 3], 1)    = [3]
     * removeElement([1, 3, 1], 1) = [3, 1]
     * </pre>
     * 
     * @param array
     *            the array to remove the element from, may be <code>null</code>
     * @param element
     *            the element to be removed
     * @return A new array containing the existing elements except the first
     *         occurrence of the specified element.
     * @since 2.1
     */
    public static short[] removeElement(short[] array, short element) {
        int index = indexOf(array, element);
        if (index == INDEX_NOT_FOUND) {
            return clone(array);
        }
        return remove(array, index);
    }

    /**
     * <p>
     * Removes the element at the specified position from the specified array.
     * All subsequent elements are shifted to the left (substracts one from
     * their indices).
     * </p>
     * 
     * <p>
     * This method returns a new array with the same elements of the input array
     * except the element on the specified position. The component type of the
     * returned array is always the same as that of the input array.
     * </p>
     * 
     * <p>
     * If the input array is <code>null</code>, an IndexOutOfBoundsException
     * will be thrown, because in that case no valid index can be specified.
     * </p>
     * 
     * @param array
     *            the array to remove the element from, may not be
     *            <code>null</code>
     * @param index
     *            the position of the element to be removed
     * @return A new array containing the existing elements except the element
     *         at the specified position.
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >=
     *             array.length), or if the array is <code>null</code>.
     * @since 2.1
     */
    private static Object remove(Object array, int index) {
        int length = getLength(array);
        if (index < 0 || index >= length) {
            throw new IndexOutOfBoundsException("Index: " + index
                    + ", Length: " + length);
        }

        Object result = Array.newInstance(array.getClass().getComponentType(),
                length - 1);
        System.arraycopy(array, 0, result, 0, index);
        if (index < length - 1) {
            System.arraycopy(array, index + 1, result, index, length - index
                    - 1);
        }

        return result;
    }

    // ///////////////////////////////////////////
    //
    // CharSequenceUtils
    //
    // ///////////////////////////////////////////

    /**
     * Gets a CharSequence length or <code>0</code> if the CharSequence is
     * <code>null</code>.
     * 
     * @param cs
     *            a CharSequence or <code>null</code>
     * @return CharSequence length or <code>0</code> if the CharSequence is
     *         <code>null</code>.
     * @since 3.0
     */
    public static int length(CharSequence cs) {
        return cs == null ? 0 : cs.length();
    }

    /**
     * Returns a new <code>CharSequence</code> that is a subsequence of this
     * sequence starting with the <code>char</code> value at the specified
     * index. The length (in <code>char</code>s) of the returned sequence is
     * <code>length() - start</code>, so if <code>start == end</code> then an
     * empty sequence is returned. </p>
     * 
     * @param cs
     *            the specified subsequence, may be null
     * @param start
     *            the start index, inclusive
     * @return a new subsequence or null
     * 
     * @throws IndexOutOfBoundsException
     *             if <code>start</code> is negative or if <code>start</code> is
     *             greater than <code>length()</code>
     * @since 3.0
     */
    public static CharSequence subSequence(CharSequence cs, int start) {
        return cs == null ? null : cs.subSequence(start, cs.length());
    }

    // ///////////////////////////////////////////
    //
    // CharUtils
    //
    // ///////////////////////////////////////////

    private static final String CHAR_STRING = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"
            + "\b\t\n\u000b\f\r\u000e\u000f"
            + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"
            + "\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"
            + "\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027"
            + "\u0028\u0029\u002a\u002b\u002c\u002d\u002e\u002f"
            + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037"
            + "\u0038\u0039\u003a\u003b\u003c\u003d\u003e\u003f"
            + "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047"
            + "\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f"
            + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057"
            + "\u0058\u0059\u005a\u005b\\\u005d\u005e\u005f"
            + "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067"
            + "\u0068\u0069\u006a\u006b\u006c\u006d\u006e\u006f"
            + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077"
            + "\u0078\u0079\u007a\u007b\u007c\u007d\u007e\u007f";

    private static final String[] CHAR_STRING_ARRAY = new String[128];
    private static final Character[] CHAR_ARRAY = new Character[128];

    /**
     * <code>\u000a</code> linefeed LF ('\n').
     * 
     * @see <a
     *      href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF:
     *      Escape Sequences for Character and String Literals</a>
     * @since 2.2
     */
    public static final char CHAR_LF = '\n';

    /**
     * <code>\u000d</code> carriage return CR ('\r').
     * 
     * @see <a
     *      href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF:
     *      Escape Sequences for Character and String Literals</a>
     * @since 2.2
     */
    public static final char CHAR_CR = '\r';

    static {
        for (int i = 127; i >= 0; i--) {
            CHAR_STRING_ARRAY[i] = CHAR_STRING.substring(i, i + 1);
            CHAR_ARRAY[i] = new Character((char) i);
        }
    }

    /**
     * <p>
     * Converts the character to a Character.
     * </p>
     * 
     * <p>
     * For ASCII 7 bit characters, this uses a cache that will return the same
     * Character object each time.
     * </p>
     * 
     * <pre>
     *   CharUtils.toCharacterObject(' ')  = ' '
     *   CharUtils.toCharacterObject('A')  = 'A'
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return a Character of the specified character
     */
    public static Character toCharacterObject(char ch) {
        if (ch < CHAR_ARRAY.length) {
            return CHAR_ARRAY[ch];
        }
        return new Character(ch);
    }

    /**
     * <p>
     * Converts the String to a Character using the first character, returning
     * null for empty Strings.
     * </p>
     * 
     * <p>
     * For ASCII 7 bit characters, this uses a cache that will return the same
     * Character object each time.
     * </p>
     * 
     * <pre>
     *   CharUtils.toCharacterObject(null) = null
     *   CharUtils.toCharacterObject("")   = null
     *   CharUtils.toCharacterObject("A")  = 'A'
     *   CharUtils.toCharacterObject("BA") = 'B'
     * </pre>
     * 
     * @param str
     *            the character to convert
     * @return the Character value of the first letter of the String
     */
    public static Character toCharacterObject(String str) {
        if (isEmpty(str)) {
            return null;
        }
        return toCharacterObject(str.charAt(0));
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Converts the Character to a char throwing an exception for
     * <code>null</code>.
     * </p>
     * 
     * <pre>
     *   CharUtils.toChar(null) = IllegalArgumentException
     *   CharUtils.toChar(' ')  = ' '
     *   CharUtils.toChar('A')  = 'A'
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return the char value of the Character
     * @throws IllegalArgumentException
     *             if the Character is null
     */
    public static char toChar(Character ch) {
        if (ch == null) {
            throw new IllegalArgumentException("The Character must not be null");
        }
        return ch.charValue();
    }

    /**
     * <p>
     * Converts the Character to a char handling <code>null</code>.
     * </p>
     * 
     * <pre>
     *   CharUtils.toChar(null, 'X') = 'X'
     *   CharUtils.toChar(' ', 'X')  = ' '
     *   CharUtils.toChar('A', 'X')  = 'A'
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @param defaultValue
     *            the value to use if the Character is null
     * @return the char value of the Character or the default if null
     */
    public static char toChar(Character ch, char defaultValue) {
        if (ch == null) {
            return defaultValue;
        }
        return ch.charValue();
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Converts the String to a char using the first character, throwing an
     * exception on empty Strings.
     * </p>
     * 
     * <pre>
     *   CharUtils.toChar(null) = IllegalArgumentException
     *   CharUtils.toChar("")   = IllegalArgumentException
     *   CharUtils.toChar("A")  = 'A'
     *   CharUtils.toChar("BA") = 'B'
     * </pre>
     * 
     * @param str
     *            the character to convert
     * @return the char value of the first letter of the String
     * @throws IllegalArgumentException
     *             if the String is empty
     */
    public static char toChar(String str) {
        if (isEmpty(str)) {
            throw new IllegalArgumentException("The String must not be empty");
        }
        return str.charAt(0);
    }

    /**
     * <p>
     * Converts the String to a char using the first character, defaulting the
     * value on empty Strings.
     * </p>
     * 
     * <pre>
     *   CharUtils.toChar(null, 'X') = 'X'
     *   CharUtils.toChar("", 'X')   = 'X'
     *   CharUtils.toChar("A", 'X')  = 'A'
     *   CharUtils.toChar("BA", 'X') = 'B'
     * </pre>
     * 
     * @param str
     *            the character to convert
     * @param defaultValue
     *            the value to use if the Character is null
     * @return the char value of the first letter of the String or the default
     *         if null
     */
    public static char toChar(String str, char defaultValue) {
        if (isEmpty(str)) {
            return defaultValue;
        }
        return str.charAt(0);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Converts the character to the Integer it represents, throwing an
     * exception if the character is not numeric.
     * </p>
     * 
     * <p>
     * This method coverts the char '1' to the int 1 and so on.
     * </p>
     * 
     * <pre>
     *   CharUtils.toIntValue('3')  = 3
     *   CharUtils.toIntValue('A')  = IllegalArgumentException
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return the int value of the character
     * @throws IllegalArgumentException
     *             if the character is not ASCII numeric
     */
    public static int toIntValue(char ch) {
        if (isAsciiNumeric(ch) == false) {
            throw new IllegalArgumentException("The character " + ch
                    + " is not in the range '0' - '9'");
        }
        return ch - 48;
    }

    /**
     * <p>
     * Converts the character to the Integer it represents, throwing an
     * exception if the character is not numeric.
     * </p>
     * 
     * <p>
     * This method coverts the char '1' to the int 1 and so on.
     * </p>
     * 
     * <pre>
     *   CharUtils.toIntValue('3', -1)  = 3
     *   CharUtils.toIntValue('A', -1)  = -1
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @param defaultValue
     *            the default value to use if the character is not numeric
     * @return the int value of the character
     */
    public static int toIntValue(char ch, int defaultValue) {
        if (isAsciiNumeric(ch) == false) {
            return defaultValue;
        }
        return ch - 48;
    }

    /**
     * <p>
     * Converts the character to the Integer it represents, throwing an
     * exception if the character is not numeric.
     * </p>
     * 
     * <p>
     * This method coverts the char '1' to the int 1 and so on.
     * </p>
     * 
     * <pre>
     *   CharUtils.toIntValue(null) = IllegalArgumentException
     *   CharUtils.toIntValue('3')  = 3
     *   CharUtils.toIntValue('A')  = IllegalArgumentException
     * </pre>
     * 
     * @param ch
     *            the character to convert, not null
     * @return the int value of the character
     * @throws IllegalArgumentException
     *             if the Character is not ASCII numeric or is null
     */
    public static int toIntValue(Character ch) {
        if (ch == null) {
            throw new IllegalArgumentException("The character must not be null");
        }
        return toIntValue(ch.charValue());
    }

    /**
     * <p>
     * Converts the character to the Integer it represents, throwing an
     * exception if the character is not numeric.
     * </p>
     * 
     * <p>
     * This method coverts the char '1' to the int 1 and so on.
     * </p>
     * 
     * <pre>
     *   CharUtils.toIntValue(null, -1) = -1
     *   CharUtils.toIntValue('3', -1)  = 3
     *   CharUtils.toIntValue('A', -1)  = -1
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @param defaultValue
     *            the default value to use if the character is not numeric
     * @return the int value of the character
     */
    public static int toIntValue(Character ch, int defaultValue) {
        if (ch == null) {
            return defaultValue;
        }
        return toIntValue(ch.charValue(), defaultValue);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Converts the character to a String that contains the one character.
     * </p>
     * 
     * <p>
     * For ASCII 7 bit characters, this uses a cache that will return the same
     * String object each time.
     * </p>
     * 
     * <pre>
     *   CharUtils.toString(' ')  = " "
     *   CharUtils.toString('A')  = "A"
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return a String containing the one specified character
     */
    public static String charToString(char ch) {
        if (ch < 128) {
            return CHAR_STRING_ARRAY[ch];
        }
        return new String(new char[] { ch });
    }

    /**
     * <p>
     * Converts the character to a String that contains the one character.
     * </p>
     * 
     * <p>
     * For ASCII 7 bit characters, this uses a cache that will return the same
     * String object each time.
     * </p>
     * 
     * <p>
     * If <code>null</code> is passed in, <code>null</code> will be returned.
     * </p>
     * 
     * <pre>
     *   CharUtils.toString(null) = null
     *   CharUtils.toString(' ')  = " "
     *   CharUtils.toString('A')  = "A"
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return a String containing the one specified character
     */
    public static String charToString(Character ch) {
        if (ch == null) {
            return null;
        }
        return charToString(ch.charValue());
    }

    // --------------------------------------------------------------------------
    /**
     * <p>
     * Converts the string to the unicode format '\u0020'.
     * </p>
     * 
     * <p>
     * This format is the Java source code format.
     * </p>
     * 
     * <pre>
     *   CharUtils.unicodeEscaped(' ') = "\u0020"
     *   CharUtils.unicodeEscaped('A') = "\u0041"
     * </pre>
     * 
     * @param ch
     *            the character to convert
     * @return the escaped unicode string
     */
    public static String unicodeEscaped(char ch) {
        if (ch < 0x10) {
            return "\\u000" + Integer.toHexString(ch);
        } else if (ch < 0x100) {
            return "\\u00" + Integer.toHexString(ch);
        } else if (ch < 0x1000) {
            return "\\u0" + Integer.toHexString(ch);
        }
        return "\\u" + Integer.toHexString(ch);
    }

    /**
     * <p>
     * Converts the string to the unicode format '\u0020'.
     * </p>
     * 
     * <p>
     * This format is the Java source code format.
     * </p>
     * 
     * <p>
     * If <code>null</code> is passed in, <code>null</code> will be returned.
     * </p>
     * 
     * <pre>
     *   CharUtils.unicodeEscaped(null) = null
     *   CharUtils.unicodeEscaped(' ')  = "\u0020"
     *   CharUtils.unicodeEscaped('A')  = "\u0041"
     * </pre>
     * 
     * @param ch
     *            the character to convert, may be null
     * @return the escaped unicode string, null if null input
     */
    public static String unicodeEscaped(Character ch) {
        if (ch == null) {
            return null;
        }
        return unicodeEscaped(ch.charValue());
    }

    // --------------------------------------------------------------------------
    /**
     * <p>
     * Checks whether the character is ASCII 7 bit.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAscii('a')  = true
     *   CharUtils.isAscii('A')  = true
     *   CharUtils.isAscii('3')  = true
     *   CharUtils.isAscii('-')  = true
     *   CharUtils.isAscii('\n') = true
     *   CharUtils.isAscii('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if less than 128
     */
    public static boolean isAscii(char ch) {
        return ch < 128;
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit printable.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiPrintable('a')  = true
     *   CharUtils.isAsciiPrintable('A')  = true
     *   CharUtils.isAsciiPrintable('3')  = true
     *   CharUtils.isAsciiPrintable('-')  = true
     *   CharUtils.isAsciiPrintable('\n') = false
     *   CharUtils.isAsciiPrintable('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 32 and 126 inclusive
     */
    public static boolean isAsciiPrintable(char ch) {
        return ch >= 32 && ch < 127;
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit control.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiControl('a')  = false
     *   CharUtils.isAsciiControl('A')  = false
     *   CharUtils.isAsciiControl('3')  = false
     *   CharUtils.isAsciiControl('-')  = false
     *   CharUtils.isAsciiControl('\n') = true
     *   CharUtils.isAsciiControl('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if less than 32 or equals 127
     */
    public static boolean isAsciiControl(char ch) {
        return ch < 32 || ch == 127;
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit alphabetic.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiAlpha('a')  = true
     *   CharUtils.isAsciiAlpha('A')  = true
     *   CharUtils.isAsciiAlpha('3')  = false
     *   CharUtils.isAsciiAlpha('-')  = false
     *   CharUtils.isAsciiAlpha('\n') = false
     *   CharUtils.isAsciiAlpha('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 65 and 90 or 97 and 122 inclusive
     */
    public static boolean isAsciiAlpha(char ch) {
        return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit alphabetic upper case.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiAlphaUpper('a')  = false
     *   CharUtils.isAsciiAlphaUpper('A')  = true
     *   CharUtils.isAsciiAlphaUpper('3')  = false
     *   CharUtils.isAsciiAlphaUpper('-')  = false
     *   CharUtils.isAsciiAlphaUpper('\n') = false
     *   CharUtils.isAsciiAlphaUpper('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 65 and 90 inclusive
     */
    public static boolean isAsciiAlphaUpper(char ch) {
        return ch >= 'A' && ch <= 'Z';
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit alphabetic lower case.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiAlphaLower('a')  = true
     *   CharUtils.isAsciiAlphaLower('A')  = false
     *   CharUtils.isAsciiAlphaLower('3')  = false
     *   CharUtils.isAsciiAlphaLower('-')  = false
     *   CharUtils.isAsciiAlphaLower('\n') = false
     *   CharUtils.isAsciiAlphaLower('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 97 and 122 inclusive
     */
    public static boolean isAsciiAlphaLower(char ch) {
        return ch >= 'a' && ch <= 'z';
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit numeric.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiNumeric('a')  = false
     *   CharUtils.isAsciiNumeric('A')  = false
     *   CharUtils.isAsciiNumeric('3')  = true
     *   CharUtils.isAsciiNumeric('-')  = false
     *   CharUtils.isAsciiNumeric('\n') = false
     *   CharUtils.isAsciiNumeric('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 48 and 57 inclusive
     */
    public static boolean isAsciiNumeric(char ch) {
        return ch >= '0' && ch <= '9';
    }

    /**
     * <p>
     * Checks whether the character is ASCII 7 bit numeric.
     * </p>
     * 
     * <pre>
     *   CharUtils.isAsciiAlphanumeric('a')  = true
     *   CharUtils.isAsciiAlphanumeric('A')  = true
     *   CharUtils.isAsciiAlphanumeric('3')  = true
     *   CharUtils.isAsciiAlphanumeric('-')  = false
     *   CharUtils.isAsciiAlphanumeric('\n') = false
     *   CharUtils.isAsciiAlphanumeric('&copy;') = false
     * </pre>
     * 
     * @param ch
     *            the character to check
     * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive
     */
    public static boolean isAsciiAlphanumeric(char ch) {
        return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
                || (ch >= '0' && ch <= '9');
    }

    // ///////////////////////////////////////////
    //
    // SystemUtils
    //
    // ///////////////////////////////////////////

    private static final int JAVA_VERSION_TRIM_SIZE = 3;

    /**
     * The prefix String for all Windows OS.
     */
    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";

    // System property constants
    // -----------------------------------------------------------------------
    // These MUST be declared first. Other constants depend on this.

    /**
     * The System property key for the user home directory.
     */
    private static final String USER_HOME_KEY = "user.home";

    /**
     * The System property key for the user directory.
     */
    private static final String USER_DIR_KEY = "user.dir";

    /**
     * The System property key for the Java IO temporary directory.
     */
    private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir";

    /**
     * The System property key for the Java home directory.
     */
    private static final String JAVA_HOME_KEY = "java.home";

    /**
     * <p>
     * The <code>awt.toolkit</code> System Property.
     * </p>
     * <p>
     * Holds a class name, on Windows XP this is
     * <code>sun.awt.windows.WToolkit</code>.
     * </p>
     * <p>
     * <b>On platforms without a GUI, this value is <code>null</code>.</b>
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     */
    public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit");

    /**
     * <p>
     * The <code>file.encoding</code> System Property.
     * </p>
     * <p>
     * File encoding, such as <code>Cp1252</code>.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.2
     */
    public static final String FILE_ENCODING = getSystemProperty("file.encoding");

    /**
     * <p>
     * The <code>file.separator</code> System Property. File separator (
     * <code>&quot;/&quot;</code> on UNIX).
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String FILE_SEPARATOR = getSystemProperty("file.separator");

    /**
     * <p>
     * The <code>java.awt.fonts</code> System Property.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     */
    public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts");

    /**
     * <p>
     * The <code>java.awt.graphicsenv</code> System Property.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     */
    public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv");

    /**
     * <p>
     * The <code>java.awt.headless</code> System Property. The value of this
     * property is the String <code>"true"</code> or <code>"false"</code>.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @see #isJavaAwtHeadless()
     * @since 2.1
     * @since Java 1.4
     */
    public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless");

    /**
     * <p>
     * The <code>java.awt.printerjob</code> System Property.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     */
    public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob");

    /**
     * <p>
     * The <code>java.class.path</code> System Property. Java class path.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path");

    /**
     * <p>
     * The <code>java.class.version</code> System Property. Java class format
     * version number.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version");

    /**
     * <p>
     * The <code>java.compiler</code> System Property. Name of JIT compiler to
     * use. First in JDK version 1.2. Not used in Sun JDKs after 1.2.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2. Not used in Sun versions after 1.2.
     */
    public static final String JAVA_COMPILER = getSystemProperty("java.compiler");

    /**
     * <p>
     * The <code>java.endorsed.dirs</code> System Property. Path of endorsed
     * directory or directories.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.4
     */
    public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs");

    /**
     * <p>
     * The <code>java.ext.dirs</code> System Property. Path of extension
     * directory or directories.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.3
     */
    public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs");

    /**
     * <p>
     * The <code>java.home</code> System Property. Java installation directory.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY);

    /**
     * <p>
     * The <code>java.io.tmpdir</code> System Property. Default temp file path.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY);

    /**
     * <p>
     * The <code>java.library.path</code> System Property. List of paths to
     * search when loading libraries.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path");

    /**
     * <p>
     * The <code>java.runtime.name</code> System Property. Java Runtime
     * Environment name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.3
     */
    public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name");

    /**
     * <p>
     * The <code>java.runtime.version</code> System Property. Java Runtime
     * Environment version.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.3
     */
    public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version");

    /**
     * <p>
     * The <code>java.specification.name</code> System Property. Java Runtime
     * Environment specification name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name");

    /**
     * <p>
     * The <code>java.specification.vendor</code> System Property. Java Runtime
     * Environment specification vendor.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor");

    /**
     * <p>
     * The <code>java.specification.version</code> System Property. Java Runtime
     * Environment specification version.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.3
     */
    public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version");

    /**
     * <p>
     * The <code>java.util.prefs.PreferencesFactory</code> System Property. A
     * class name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     * @since Java 1.4
     */
    public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY = getSystemProperty("java.util.prefs.PreferencesFactory");

    /**
     * <p>
     * The <code>java.vendor</code> System Property. Java vendor-specific
     * string.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_VENDOR = getSystemProperty("java.vendor");

    /**
     * <p>
     * The <code>java.vendor.url</code> System Property. Java vendor URL.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url");

    /**
     * <p>
     * The <code>java.version</code> System Property. Java version number.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String JAVA_VERSION = getSystemProperty("java.version");

    /**
     * <p>
     * The <code>java.vm.info</code> System Property. Java Virtual Machine
     * implementation info.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.2
     */
    public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info");

    /**
     * <p>
     * The <code>java.vm.name</code> System Property. Java Virtual Machine
     * implementation name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name");

    /**
     * <p>
     * The <code>java.vm.specification.name</code> System Property. Java Virtual
     * Machine specification name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name");

    /**
     * <p>
     * The <code>java.vm.specification.vendor</code> System Property. Java
     * Virtual Machine specification vendor.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor");

    /**
     * <p>
     * The <code>java.vm.specification.version</code> System Property. Java
     * Virtual Machine specification version.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version");

    /**
     * <p>
     * The <code>java.vm.vendor</code> System Property. Java Virtual Machine
     * implementation vendor.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor");

    /**
     * <p>
     * The <code>java.vm.version</code> System Property. Java Virtual Machine
     * implementation version.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.2
     */
    public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version");

    /**
     * <p>
     * The <code>line.separator</code> System Property. Line separator (
     * <code>&quot;\n&quot;</code> on UNIX).
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String LINE_SEPARATOR = getSystemProperty("line.separator");

    /**
     * <p>
     * The <code>os.arch</code> System Property. Operating system architecture.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String OS_ARCH = getSystemProperty("os.arch");

    /**
     * <p>
     * The <code>os.name</code> System Property. Operating system name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String OS_NAME = getSystemProperty("os.name");

    /**
     * <p>
     * The <code>os.version</code> System Property. Operating system version.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String OS_VERSION = getSystemProperty("os.version");

    /**
     * <p>
     * The <code>path.separator</code> System Property. Path separator (
     * <code>&quot;:&quot;</code> on UNIX).
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String PATH_SEPARATOR = getSystemProperty("path.separator");

    /**
     * <p>
     * The <code>user.country</code> or <code>user.region</code> System
     * Property. User's country code, such as <code>GB</code>. First in Java
     * version 1.2 as <code>user.region</code>. Renamed to
     * <code>user.country</code> in 1.4
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.2
     */
    public static final String USER_COUNTRY = getSystemProperty("user.country") == null ? getSystemProperty("user.region")
            : getSystemProperty("user.country");

    /**
     * <p>
     * The <code>user.dir</code> System Property. User's current working
     * directory.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String USER_DIR = getSystemProperty(USER_DIR_KEY);

    /**
     * <p>
     * The <code>user.home</code> System Property. User's home directory.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String USER_HOME = getSystemProperty(USER_HOME_KEY);

    /**
     * <p>
     * The <code>user.language</code> System Property. User's language code,
     * such as <code>"en"</code>.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.0
     * @since Java 1.2
     */
    public static final String USER_LANGUAGE = getSystemProperty("user.language");

    /**
     * <p>
     * The <code>user.name</code> System Property. User's account name.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since Java 1.1
     */
    public static final String USER_NAME = getSystemProperty("user.name");

    /**
     * <p>
     * The <code>user.timezone</code> System Property. For example:
     * <code>"America/Los_Angeles"</code>.
     * </p>
     * 
     * <p>
     * Defaults to <code>null</code> if the runtime does not have security
     * access to read this property or the property does not exist.
     * </p>
     * 
     * <p>
     * This value is initialized when the class is loaded. If
     * {@link System#setProperty(String,String)} or
     * {@link System#setProperties(java.util.Properties)} is called after this
     * class is loaded, the value will be out of sync with that System property.
     * </p>
     * 
     * @since 2.1
     */
    public static final String USER_TIMEZONE = getSystemProperty("user.timezone");

    // Java version
    // -----------------------------------------------------------------------
    // This MUST be declared after those above as it depends on the
    // values being set up

    /**
     * <p>
     * Gets the Java version as a <code>String</code> trimming leading letters.
     * </p>
     * 
     * <p>
     * The field will return <code>null</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.1
     */
    public static final String JAVA_VERSION_TRIMMED = getJavaVersionTrimmed();

    // Java version values
    // -----------------------------------------------------------------------
    // These MUST be declared after the trim above as they depend on the
    // value being set up

    /**
     * <p>
     * Gets the Java version as a <code>float</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>1.2f</code> for Java 1.2
     * <li><code>1.31f</code> for Java 1.3.1
     * </ul>
     * 
     * <p>
     * The field will return zero if {@link #JAVA_VERSION} is <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final float JAVA_VERSION_FLOAT = getJavaVersionAsFloat();

    /**
     * <p>
     * Gets the Java version as an <code>int</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>120</code> for Java 1.2
     * <li><code>131</code> for Java 1.3.1
     * </ul>
     * 
     * <p>
     * The field will return zero if {@link #JAVA_VERSION} is <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final int JAVA_VERSION_INT = getJavaVersionAsInt();

    // Java version checks
    // -----------------------------------------------------------------------
    // These MUST be declared after those above as they depend on the
    // values being set up

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.1 (also 1.1.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.2 (also 1.2.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.3 (also 1.3.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.4 (also 1.4.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.5 (also 1.5.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.6 (also 1.6.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     */
    public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6");

    /**
     * <p>
     * Is <code>true</code> if this is Java version 1.7 (also 1.7.x versions).
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if {@link #JAVA_VERSION} is
     * <code>null</code>.
     * </p>
     * 
     * @since 3.0
     */
    public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7");

    // Operating system checks
    // -----------------------------------------------------------------------
    // These MUST be declared after those above as they depend on the
    // values being set up
    // OS names from http://www.vamphq.com/os.html
    // Selected ones included - please advise dev@commons.apache.org
    // if you want another added or a mistake corrected

    /**
     * <p>
     * Is <code>true</code> if this is AIX.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_AIX = getOSMatchesName("AIX");

    /**
     * <p>
     * Is <code>true</code> if this is HP-UX.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX");

    /**
     * <p>
     * Is <code>true</code> if this is Irix.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_IRIX = getOSMatchesName("Irix");

    /**
     * <p>
     * Is <code>true</code> if this is Linux.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_LINUX = getOSMatchesName("Linux")
            || getOSMatchesName("LINUX");

    /**
     * <p>
     * Is <code>true</code> if this is Mac.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_MAC = getOSMatchesName("Mac");

    /**
     * <p>
     * Is <code>true</code> if this is Mac.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X");

    /**
     * <p>
     * Is <code>true</code> if this is OS/2.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2");

    /**
     * <p>
     * Is <code>true</code> if this is Solaris.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris");

    /**
     * <p>
     * Is <code>true</code> if this is SunOS.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS");

    /**
     * <p>
     * Is <code>true</code> if this is a POSIX compilant system, as in any of
     * AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.1
     */
    public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX
            || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX || IS_OS_SOLARIS
            || IS_OS_SUN_OS;

    /**
     * <p>
     * Is <code>true</code> if this is Windows.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX);

    /**
     * <p>
     * Is <code>true</code> if this is Windows 2000.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_2000 = getOSMatches(
            OS_NAME_WINDOWS_PREFIX, "5.0");

    /**
     * <p>
     * Is <code>true</code> if this is Windows 95.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_95 = getOSMatches(
            OS_NAME_WINDOWS_PREFIX + " 9", "4.0");
    // Java 1.2 running on Windows98 returns 'Windows 95', hence the above

    /**
     * <p>
     * Is <code>true</code> if this is Windows 98.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_98 = getOSMatches(
            OS_NAME_WINDOWS_PREFIX + " 9", "4.1");
    // Java 1.2 running on Windows98 returns 'Windows 95', hence the above

    /**
     * <p>
     * Is <code>true</code> if this is Windows ME.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_ME = getOSMatches(
            OS_NAME_WINDOWS_PREFIX, "4.9");
    // Java 1.2 running on WindowsME may return 'Windows 95', hence the above

    /**
     * <p>
     * Is <code>true</code> if this is Windows NT.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX
            + " NT");
    // Windows 2000 returns 'Windows 2000' but may suffer from same Java1.2
    // problem

    /**
     * <p>
     * Is <code>true</code> if this is Windows XP.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.0
     */
    public static final boolean IS_OS_WINDOWS_XP = getOSMatches(
            OS_NAME_WINDOWS_PREFIX, "5.1");

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Is <code>true</code> if this is Windows Vista.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 2.4
     */
    public static final boolean IS_OS_WINDOWS_VISTA = getOSMatches(
            OS_NAME_WINDOWS_PREFIX, "6.0");

    /**
     * <p>
     * Is <code>true</code> if this is Windows 7.
     * </p>
     * 
     * <p>
     * The field will return <code>false</code> if <code>OS_NAME</code> is
     * <code>null</code>.
     * </p>
     * 
     * @since 3.0
     */
    public static final boolean IS_OS_WINDOWS_7 = getOSMatches(
            OS_NAME_WINDOWS_PREFIX, "6.1");

    /**
     * <p>
     * Gets the Java home directory as a <code>File</code>.
     * </p>
     * 
     * @return a directory
     * @throws SecurityException
     *             if a security manager exists and its
     *             <code>checkPropertyAccess</code> method doesn't allow access
     *             to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getJavaHome() {
        return new File(System.getProperty(JAVA_HOME_KEY));
    }

    /**
     * <p>
     * Gets the Java IO temporary directory as a <code>File</code>.
     * </p>
     * 
     * @return a directory
     * @throws SecurityException
     *             if a security manager exists and its
     *             <code>checkPropertyAccess</code> method doesn't allow access
     *             to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getJavaIoTmpDir() {
        return new File(System.getProperty(JAVA_IO_TMPDIR_KEY));
    }

    /**
     * <p>
     * Gets the Java version number as a <code>float</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>1.2f</code> for Java 1.2</li>
     * <li><code>1.31f</code> for Java 1.3.1</li>
     * <li><code>1.6f</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * 
     * @return the version, for example 1.31f for Java 1.3.1
     */
    private static float getJavaVersionAsFloat() {
        return toVersionFloat(toJavaVersionIntArray(JAVA_VERSION,
                JAVA_VERSION_TRIM_SIZE));
    }

    /**
     * <p>
     * Gets the Java version number as an <code>int</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>120</code> for Java 1.2</li>
     * <li><code>131</code> for Java 1.3.1</li>
     * <li><code>160</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * 
     * @return the version, for example 131 for Java 1.3.1
     */
    private static int getJavaVersionAsInt() {
        return toVersionInt(toJavaVersionIntArray(JAVA_VERSION,
                JAVA_VERSION_TRIM_SIZE));
    }

    /**
     * <p>
     * Decides if the Java version matches.
     * </p>
     * 
     * @param versionPrefix
     *            the prefix for the java version
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getJavaVersionMatches(String versionPrefix) {
        return isJavaVersionMatch(JAVA_VERSION_TRIMMED, versionPrefix);
    }

    /**
     * Trims the text of the java version to start with numbers.
     * 
     * @return the trimmed java version
     */
    private static String getJavaVersionTrimmed() {
        if (JAVA_VERSION != null) {
            for (int i = 0; i < JAVA_VERSION.length(); i++) {
                char ch = JAVA_VERSION.charAt(i);
                if (ch >= '0' && ch <= '9') {
                    return JAVA_VERSION.substring(i);
                }
            }
        }
        return null;
    }

    /**
     * Decides if the operating system matches.
     * 
     * @param osNamePrefix
     *            the prefix for the os name
     * @param osVersionPrefix
     *            the prefix for the version
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getOSMatches(String osNamePrefix,
            String osVersionPrefix) {
        return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix);
    }

    /**
     * Decides if the operating system matches.
     * 
     * @param osNamePrefix
     *            the prefix for the os name
     * @return true if matches, or false if not or can't determine
     */
    private static boolean getOSMatchesName(String osNamePrefix) {
        return isOSNameMatch(OS_NAME, osNamePrefix);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets a System property, defaulting to <code>null</code> if the property
     * cannot be read.
     * </p>
     * 
     * <p>
     * If a <code>SecurityException</code> is caught, the return value is
     * <code>null</code> and a message is written to <code>System.err</code>.
     * </p>
     * 
     * @param property
     *            the system property name
     * @return the system property value or <code>null</code> if a security
     *         problem occurs
     */
    private static String getSystemProperty(String property) {
        try {
            return System.getProperty(property);
        } catch (SecurityException ex) {
            // we are not allowed to look at this property
            System.err
                    .println("Caught a SecurityException reading the system property '"
                            + property
                            + "'; the SystemUtils property value will default to null.");
            return null;
        }
    }

    /**
     * <p>
     * Gets the user directory as a <code>File</code>.
     * </p>
     * 
     * @return a directory
     * @throws SecurityException
     *             if a security manager exists and its
     *             <code>checkPropertyAccess</code> method doesn't allow access
     *             to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getUserDir() {
        return new File(System.getProperty(USER_DIR_KEY));
    }

    /**
     * <p>
     * Gets the user home directory as a <code>File</code>.
     * </p>
     * 
     * @return a directory
     * @throws SecurityException
     *             if a security manager exists and its
     *             <code>checkPropertyAccess</code> method doesn't allow access
     *             to the specified system property.
     * @see System#getProperty(String)
     * @since 2.1
     */
    public static File getUserHome() {
        return new File(System.getProperty(USER_HOME_KEY));
    }

    /**
     * Returns whether the {@link #JAVA_AWT_HEADLESS} value is <code>true</code>
     * .
     * 
     * @return <code>true</code> if <code>JAVA_AWT_HEADLESS</code> is
     *         <code>"true"</code>, <code>false</code> otherwise.
     * 
     * @see #JAVA_AWT_HEADLESS
     * @since 2.1
     * @since Java 1.4
     */
    public static boolean isJavaAwtHeadless() {
        return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS
                .equals(Boolean.TRUE.toString()) : false;
    }

    /**
     * <p>
     * Is the Java version at least the requested version.
     * </p>
     * 
     * <p>
     * Example input:
     * </p>
     * <ul>
     * <li><code>1.2f</code> to test for Java 1.2</li>
     * <li><code>1.31f</code> to test for Java 1.3.1</li>
     * </ul>
     * 
     * @param requiredVersion
     *            the required version, for example 1.31f
     * @return <code>true</code> if the actual version is equal or greater than
     *         the required version
     */
    public static boolean isJavaVersionAtLeast(float requiredVersion) {
        return JAVA_VERSION_FLOAT >= requiredVersion;
    }

    /**
     * <p>
     * Is the Java version at least the requested version.
     * </p>
     * 
     * <p>
     * Example input:
     * </p>
     * <ul>
     * <li><code>120</code> to test for Java 1.2 or greater</li>
     * <li><code>131</code> to test for Java 1.3.1 or greater</li>
     * </ul>
     * 
     * @param requiredVersion
     *            the required version, for example 131
     * @return <code>true</code> if the actual version is equal or greater than
     *         the required version
     * @since 2.0
     */
    public static boolean isJavaVersionAtLeast(int requiredVersion) {
        return JAVA_VERSION_INT >= requiredVersion;
    }

    /**
     * <p>
     * Decides if the Java version matches.
     * </p>
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @param version
     *            the actual Java version
     * @param versionPrefix
     *            the prefix for the expected Java version
     * @return true if matches, or false if not or can't determine
     */
    static boolean isJavaVersionMatch(String version, String versionPrefix) {
        if (version == null) {
            return false;
        }
        return version.startsWith(versionPrefix);
    }

    /**
     * Decides if the operating system matches.
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @param osName
     *            the actual OS name
     * @param osVersion
     *            the actual OS version
     * @param osNamePrefix
     *            the prefix for the expected OS name
     * @param osVersionPrefix
     *            the prefix for the expected OS version
     * @return true if matches, or false if not or can't determine
     */
    static boolean isOSMatch(String osName, String osVersion,
            String osNamePrefix, String osVersionPrefix) {
        if (osName == null || osVersion == null) {
            return false;
        }
        return osName.startsWith(osNamePrefix)
                && osVersion.startsWith(osVersionPrefix);
    }

    /**
     * Decides if the operating system matches.
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @param osName
     *            the actual OS name
     * @param osNamePrefix
     *            the prefix for the expected OS name
     * @return true if matches, or false if not or can't determine
     */
    static boolean isOSNameMatch(String osName, String osNamePrefix) {
        if (osName == null) {
            return false;
        }
        return osName.startsWith(osNamePrefix);
    }

    /**
     * <p>
     * Converts the given Java version string to a <code>float</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>1.2f</code> for Java 1.2</li>
     * <li><code>1.31f</code> for Java 1.3.1</li>
     * <li><code>1.6f</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @return the version, for example 1.31f for Java 1.3.1
     */
    static float toJavaVersionFloat(String version) {
        return toVersionFloat(toJavaVersionIntArray(version,
                JAVA_VERSION_TRIM_SIZE));
    }

    /**
     * <p>
     * Converts the given Java version string to an <code>int</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>120</code> for Java 1.2</li>
     * <li><code>131</code> for Java 1.3.1</li>
     * <li><code>160</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @return the version, for example 131 for Java 1.3.1
     */
    static int toJavaVersionInt(String version) {
        return toVersionInt(toJavaVersionIntArray(version,
                JAVA_VERSION_TRIM_SIZE));
    }

    /**
     * <p>
     * Converts the given Java version string to an <code>int[]</code> of
     * maximum size <code>3</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>[1, 2, 0]</code> for Java 1.2</li>
     * <li><code>[1, 3, 1]</code> for Java 1.3.1</li>
     * <li><code>[1, 5, 0]</code> for Java 1.5.0_21</li>
     * </ul>
     * <p>
     * This method is package private instead of private to support unit test
     * invocation.
     * </p>
     * 
     * @return the version, for example [1, 5, 0] for Java 1.5.0_21
     */
    static int[] toJavaVersionIntArray(String version) {
        return toJavaVersionIntArray(version, Integer.MAX_VALUE);
    }

    /**
     * <p>
     * Converts the given Java version string to an <code>int[]</code> of
     * maximum size <code>limit</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>[1, 2, 0]</code> for Java 1.2</li>
     * <li><code>[1, 3, 1]</code> for Java 1.3.1</li>
     * <li><code>[1, 5, 0, 21]</code> for Java 1.5.0_21</li>
     * </ul>
     * 
     * @return the version, for example [1, 5, 0, 21] for Java 1.5.0_21
     */
    private static int[] toJavaVersionIntArray(String version, int limit) {
        if (version == null) {
            return EMPTY_INT_ARRAY;
        }
        String[] strings = Pattern.compile("[^\\d]").split(version);
        int[] ints = new int[Math.min(limit, strings.length)];
        int j = 0;
        for (int i = 0; i < strings.length && j < limit; i++) {
            String s = strings[i];
            if (s.length() > 0) {
                ints[j++] = Integer.parseInt(s);
            }
        }
        return ints;
    }

    /**
     * <p>
     * Converts given the Java version array to a <code>float</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>1.2f</code> for Java 1.2</li>
     * <li><code>1.31f</code> for Java 1.3.1</li>
     * <li><code>1.6f</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * 
     * @return the version, for example 1.31f for Java 1.3.1
     */
    private static float toVersionFloat(int[] javaVersions) {
        if (javaVersions == null || javaVersions.length == 0) {
            return 0f;
        }
        if (javaVersions.length == 1) {
            return javaVersions[0];
        }
        StringBuilder builder = new StringBuilder();
        builder.append(javaVersions[0]);
        builder.append('.');
        for (int i = 1; i < javaVersions.length; i++) {
            builder.append(javaVersions[i]);
        }
        try {
            return Float.parseFloat(builder.toString());
        } catch (Exception ex) {
            return 0f;
        }
    }

    /**
     * <p>
     * Converts given the Java version array to an <code>int</code>.
     * </p>
     * 
     * <p>
     * Example return values:
     * </p>
     * <ul>
     * <li><code>120</code> for Java 1.2</li>
     * <li><code>131</code> for Java 1.3.1</li>
     * <li><code>160</code> for Java 1.6.0_20</li>
     * </ul>
     * 
     * <p>
     * Patch releases are not reported.
     * </p>
     * 
     * @return the version, for example 1.31f for Java 1.3.1
     */
    private static int toVersionInt(int[] javaVersions) {
        if (javaVersions == null) {
            return 0;
        }
        int intVersion = 0;
        int len = javaVersions.length;
        if (len >= 1) {
            intVersion = javaVersions[0] * 100;
        }
        if (len >= 2) {
            intVersion += javaVersions[1] * 10;
        }
        if (len >= 3) {
            intVersion += javaVersions[2];
        }
        return intVersion;
    }

    // ///////////////////////////////////////////
    //
    // ObjectUtils
    //
    // ///////////////////////////////////////////

    public static class Null implements Serializable {
        /**
         * Required for serialization support. Declare serialization
         * compatibility with Commons Lang 1.0
         * 
         * @see java.io.Serializable
         */
        private static final long serialVersionUID = 7092611880189329093L;

        /**
         * Restricted constructor - singleton.
         */
        Null() {
            super();
        }

        /**
         * <p>
         * Ensure singleton.
         * </p>
         * 
         * @return the singleton value
         */
        private Object readResolve() {
            return NULL;
        }
    }

    /**
     * <p>
     * Singleton used as a <code>null</code> placeholder where <code>null</code>
     * has another meaning.
     * </p>
     * 
     * <p>
     * For example, in a <code>HashMap</code> the
     * {@link java.util.HashMap#get(java.lang.Object)} method returns
     * <code>null</code> if the <code>Map</code> contains <code>null</code> or
     * if there is no matching key. The <code>Null</code> placeholder can be
     * used to distinguish between these two cases.
     * </p>
     * 
     * <p>
     * Another example is <code>Hashtable</code>, where <code>null</code> cannot
     * be stored.
     * </p>
     * 
     * <p>
     * This instance is Serializable.
     * </p>
     */
    public static final Null NULL = new Null();

    /**
     * <p>
     * Returns a default value if the object passed is <code>null</code>.
     * </p>
     * 
     * <pre>
     * ObjectUtils.defaultIfNull(null, null)      = null
     * ObjectUtils.defaultIfNull(null, "")        = ""
     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
     * </pre>
     * 
     * @param object
     *            the <code>Object</code> to test, may be <code>null</code>
     * @param defaultValue
     *            the default value to return, may be <code>null</code>
     * @return <code>object</code> if it is not <code>null</code>, defaultValue
     *         otherwise
     */
    public static Object defaultIfNull(Object object, Object defaultValue) {
        return object != null ? object : defaultValue;
    }

    /**
     * <p>
     * Returns the first value in the array which is not <code>null</code>. If
     * all the values are <code>null</code> or the array is <code>null</code> or
     * empty then <code>null</code> is returned.
     * </p>
     * 
     * <pre>
     * ObjectUtils.firstNonNull(null, null)      = null
     * ObjectUtils.firstNonNull(null, "")        = ""
     * ObjectUtils.firstNonNull(null, null, "")  = ""
     * ObjectUtils.firstNonNull(null, "zz")      = "zz"
     * ObjectUtils.firstNonNull("abc", *)        = "abc"
     * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
     * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
     * ObjectUtils.firstNonNull()                = null
     * </pre>
     * 
     * @param values
     *            the values to test, may be <code>null</code> or empty
     * @return the first value from <code>values</code> which is not
     *         <code>null</code>, or <code>null</code> if there are no non-null
     *         values
     */
    public static <T> T firstNonNull(T... values) {
        if (values != null) {
            for (T val : values) {
                if (val != null) {
                    return val;
                }
            }
        }
        return null;
    }

    // Null-safe equals/hashCode
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Compares two objects for equality, where either one or both objects may
     * be <code>null</code>.
     * </p>
     * 
     * <pre>
     * ObjectUtils.equals(null, null)                  = true
     * ObjectUtils.equals(null, "")                    = false
     * ObjectUtils.equals("", null)                    = false
     * ObjectUtils.equals("", "")                      = true
     * ObjectUtils.equals(Boolean.TRUE, null)          = false
     * ObjectUtils.equals(Boolean.TRUE, "true")        = false
     * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE)  = true
     * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
     * </pre>
     * 
     * @param object1
     *            the first object, may be <code>null</code>
     * @param object2
     *            the second object, may be <code>null</code>
     * @return <code>true</code> if the values of both objects are the same
     */
    public static boolean objectEquals(Object object1, Object object2) {
        if (object1 == object2) {
            return true;
        }
        if ((object1 == null) || (object2 == null)) {
            return false;
        }
        return object1.equals(object2);
    }

    /**
     * <p>
     * Gets the hash code of an object returning zero when the object is
     * <code>null</code>.
     * </p>
     * 
     * <pre>
     * ObjectUtils.hashCode(null)   = 0
     * ObjectUtils.hashCode(obj)    = obj.hashCode()
     * </pre>
     * 
     * @param obj
     *            the object to obtain the hash code of, may be
     *            <code>null</code>
     * @return the hash code of the object, or zero if null
     * @since 2.1
     */
    public static int objectHashCode(Object obj) {
        return (obj == null) ? 0 : obj.hashCode();
    }

    // Identity ToString
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the toString that would be produced by <code>Object</code> if a
     * class did not override toString itself. <code>null</code> will return
     * <code>null</code>.
     * </p>
     * 
     * <pre>
     * ObjectUtils.identityToString(null)         = null
     * ObjectUtils.identityToString("")           = "java.lang.String@1e23"
     * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
     * </pre>
     * 
     * @param object
     *            the object to create a toString for, may be <code>null</code>
     * @return the default toString text, or <code>null</code> if
     *         <code>null</code> passed in
     */
    public static String identityToString(Object object) {
        if (object == null) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        identityToString(buffer, object);
        return buffer.toString();
    }

    /**
     * <p>
     * Appends the toString that would be produced by <code>Object</code> if a
     * class did not override toString itself. <code>null</code> will throw a
     * NullPointerException for either of the two parameters.
     * </p>
     * 
     * <pre>
     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
     * </pre>
     * 
     * @param buffer
     *            the buffer to append to
     * @param object
     *            the object to create a toString for
     * @since 2.4
     */
    public static void identityToString(StringBuffer buffer, Object object) {
        if (object == null) {
            throw new NullPointerException(
                    "Cannot get the toString of a null identity");
        }
        buffer.append(object.getClass().getName()).append('@')
                .append(Integer.toHexString(System.identityHashCode(object)));
    }

    // ToString
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the <code>toString</code> of an <code>Object</code> returning an
     * empty string ("") if <code>null</code> input.
     * </p>
     * 
     * <pre>
     * ObjectUtils.toString(null)         = ""
     * ObjectUtils.toString("")           = ""
     * ObjectUtils.toString("bat")        = "bat"
     * ObjectUtils.toString(Boolean.TRUE) = "true"
     * </pre>
     * 
     * @see defaultString(String)
     * @see String#valueOf(Object)
     * @param obj
     *            the Object to <code>toString</code>, may be null
     * @return the passed in Object's toString, or nullStr if <code>null</code>
     *         input
     * @since 2.0
     */
    public static String objectToString(Object obj) {
        return obj == null ? "" : obj.toString();
    }

    /**
     * <p>
     * Gets the <code>toString</code> of an <code>Object</code> returning a
     * specified text if <code>null</code> input.
     * </p>
     * 
     * <pre>
     * ObjectUtils.toString(null, null)           = null
     * ObjectUtils.toString(null, "null")         = "null"
     * ObjectUtils.toString("", "null")           = ""
     * ObjectUtils.toString("bat", "null")        = "bat"
     * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
     * </pre>
     * 
     * @see defaultString(String,String)
     * @see String#valueOf(Object)
     * @param obj
     *            the Object to <code>toString</code>, may be null
     * @param nullStr
     *            the String to return if <code>null</code> input, may be null
     * @return the passed in Object's toString, or nullStr if <code>null</code>
     *         input
     * @since 2.0
     */
    public static String objectToString(Object obj, String nullStr) {
        return obj == null ? nullStr : obj.toString();
    }

    // Min/Max
    // -----------------------------------------------------------------------
    /**
     * Null safe comparison of Comparables.
     * 
     * @param c1
     *            the first comparable, may be null
     * @param c2
     *            the second comparable, may be null
     * @return <ul>
     *         <li>If both objects are non-null and unequal, the lesser object.
     *         <li>If both objects are non-null and equal, c1.
     *         <li>If one of the comparables is null, the non-null object.
     *         <li>If both the comparables are null, null is returned.
     *         </ul>
     */
    public static <T extends Comparable<? super T>> T objectMin(T c1, T c2) {
        if (c1 != null && c2 != null) {
            return c1.compareTo(c2) < 1 ? c1 : c2;
        } else {
            return c1 != null ? c1 : c2;
        }
    }

    /**
     * Null safe comparison of Comparables.
     * 
     * @param c1
     *            the first comparable, may be null
     * @param c2
     *            the second comparable, may be null
     * @return <ul>
     *         <li>If both objects are non-null and unequal, the greater object.
     *         <li>If both objects are non-null and equal, c1.
     *         <li>If one of the comparables is null, the non-null object.
     *         <li>If both the comparables are null, null is returned.
     *         </ul>
     */
    public static <T extends Comparable<? super T>> T objectMax(T c1, T c2) {
        if (c1 != null && c2 != null) {
            return c1.compareTo(c2) >= 0 ? c1 : c2;
        } else {
            return c1 != null ? c1 : c2;
        }
    }

    /**
     * Clone an object.
     * 
     * @param <T>
     *            the type of the object
     * @param o
     *            the object to clone
     * @return the clone if the object implements {@link Cloneable} otherwise
     *         <code>null</code>
     * @throws CloneFailedException
     *             if the object is cloneable and the clone operation fails
     * @since 3.0
     */
    public static <T> T objectClone(final T o) {
        if (o instanceof Cloneable) {
            final Object result;
            if (o.getClass().isArray()) {
                final Class<?> componentType = o.getClass().getComponentType();
                if (!componentType.isPrimitive()) {
                    result = ((Object[]) o).clone();
                } else {
                    int length = Array.getLength(o);
                    result = Array.newInstance(componentType, length);
                    while (length-- > 0) {
                        Array.set(result, length, Array.get(o, length));
                    }
                }
            } else {
                try {
                    final Method clone = o.getClass().getMethod("clone");
                    result = clone.invoke(o);
                } catch (final NoSuchMethodException e) {
                    throw new CloneFailedException("Cloneable type "
                            + o.getClass().getName() + " has no clone method",
                            e);
                } catch (final IllegalAccessException e) {
                    throw new CloneFailedException(
                            "Cannot clone Cloneable type "
                                    + o.getClass().getName(), e);
                } catch (final InvocationTargetException e) {
                    throw new CloneFailedException(
                            "Exception cloning Cloneable type "
                                    + o.getClass().getName(), e.getCause());
                }
            }
            @SuppressWarnings("unchecked")
            final T checked = (T) result;
            return checked;
        }

        return null;
    }

    /**
     * Clone an object if possible. This method is similar to
     * {@link #objectClone(Object)}, but will return the provided instance as
     * the return value instead of <code>null</code> if the instance is not
     * cloneable. This is more convenient if the caller uses different
     * implementations (e.g. of a service) and some of the implementations do
     * not allow concurrent processing or have state. In such cases the
     * implementation can simply provide a proper clone implementation and the
     * caller's code does not have to change.
     * 
     * @param <T>
     *            the type of the object
     * @param o
     *            the object to clone
     * @return the clone if the object implements {@link Cloneable} otherwise
     *         the object itself
     * @throws CloneFailedException
     *             if the object is cloneable and the clone operation fails
     * @since 3.0
     */
    public static <T> T cloneIfPossible(final T o) {
        final T clone = objectClone(o);
        return clone == null ? o : clone;
    }

    // ///////////////////////////////////////////
    //
    // ClassUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * The package separator character: <code>'&#x2e;' == {@value}</code>.
     * </p>
     */
    public static final char PACKAGE_SEPARATOR_CHAR = '.';

    /**
     * <p>
     * The package separator String: <code>"&#x2e;"</code>.
     * </p>
     */
    public static final String PACKAGE_SEPARATOR = String
            .valueOf(PACKAGE_SEPARATOR_CHAR);

    /**
     * <p>
     * The inner class separator character: <code>'$' == {@value}</code>.
     * </p>
     */
    public static final char INNER_CLASS_SEPARATOR_CHAR = '$';

    /**
     * <p>
     * The inner class separator String: <code>"$"</code>.
     * </p>
     */
    public static final String INNER_CLASS_SEPARATOR = String
            .valueOf(INNER_CLASS_SEPARATOR_CHAR);

    /**
     * Maps primitive <code>Class</code>es to their corresponding wrapper
     * <code>Class</code>.
     */
    private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>();
    static {
        primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
        primitiveWrapperMap.put(Byte.TYPE, Byte.class);
        primitiveWrapperMap.put(Character.TYPE, Character.class);
        primitiveWrapperMap.put(Short.TYPE, Short.class);
        primitiveWrapperMap.put(Integer.TYPE, Integer.class);
        primitiveWrapperMap.put(Long.TYPE, Long.class);
        primitiveWrapperMap.put(Double.TYPE, Double.class);
        primitiveWrapperMap.put(Float.TYPE, Float.class);
        primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
    }

    /**
     * Maps wrapper <code>Class</code>es to their corresponding primitive types.
     */
    private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>();
    static {
        for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) {
            Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass);
            if (!primitiveClass.equals(wrapperClass)) {
                wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
            }
        }
    }

    /**
     * Maps a primitive class name to its corresponding abbreviation used in
     * array class names.
     */
    private static final Map<String, String> abbreviationMap = new HashMap<String, String>();

    /**
     * Maps an abbreviation used in array class names to corresponding primitive
     * class name.
     */
    private static final Map<String, String> reverseAbbreviationMap = new HashMap<String, String>();

    /**
     * Add primitive type abbreviation to maps of abbreviations.
     * 
     * @param primitive
     *            Canonical name of primitive type
     * @param abbreviation
     *            Corresponding abbreviation of primitive type
     */
    private static void addAbbreviation(String primitive, String abbreviation) {
        abbreviationMap.put(primitive, abbreviation);
        reverseAbbreviationMap.put(abbreviation, primitive);
    }

    /**
     * Feed abbreviation maps
     */
    static {
        addAbbreviation("int", "I");
        addAbbreviation("boolean", "Z");
        addAbbreviation("float", "F");
        addAbbreviation("long", "J");
        addAbbreviation("short", "S");
        addAbbreviation("byte", "B");
        addAbbreviation("double", "D");
        addAbbreviation("char", "C");
    }

    /**
     * <p>
     * Gets the class name minus the package name for an <code>Object</code>.
     * </p>
     * 
     * @param object
     *            the class to get the short name for, may be null
     * @param valueIfNull
     *            the value to return if null
     * @return the class name of the object without the package name, or the
     *         null value
     */
    public static String getShortClassName(Object object, String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getShortClassName(object.getClass());
    }

    /**
     * <p>
     * Gets the class name minus the package name from a <code>Class</code>.
     * </p>
     * 
     * @param cls
     *            the class to get the short name for.
     * @return the class name without the package name or an empty string
     */
    public static String getShortClassName(Class<?> cls) {
        if (cls == null) {
            return EMPTY_STRING;
        }
        return getShortClassName(cls.getName());
    }

    /**
     * <p>
     * Gets the class name minus the package name from a String.
     * </p>
     * 
     * <p>
     * The string passed in is assumed to be a class name - it is not checked.
     * </p>
     * 
     * @param className
     *            the className to get the short name for
     * @return the class name of the class without the package name or an empty
     *         string
     */
    public static String getShortClassName(String className) {
        if (className == null) {
            return EMPTY_STRING;
        }
        if (className.length() == 0) {
            return EMPTY_STRING;
        }

        StringBuilder arrayPrefix = new StringBuilder();

        // Handle array encoding
        if (className.startsWith("[")) {
            while (className.charAt(0) == '[') {
                className = className.substring(1);
                arrayPrefix.append("[]");
            }
            // Strip Object type encoding
            if (className.charAt(0) == 'L'
                    && className.charAt(className.length() - 1) == ';') {
                className = className.substring(1, className.length() - 1);
            }
        }

        if (reverseAbbreviationMap.containsKey(className)) {
            className = reverseAbbreviationMap.get(className);
        }

        int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
        int innerIdx = className.indexOf(INNER_CLASS_SEPARATOR_CHAR,
                lastDotIdx == -1 ? 0 : lastDotIdx + 1);
        String out = className.substring(lastDotIdx + 1);
        if (innerIdx != -1) {
            out = out.replace(INNER_CLASS_SEPARATOR_CHAR,
                    PACKAGE_SEPARATOR_CHAR);
        }
        return out + arrayPrefix;
    }

    // Package name
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Gets the package name of an <code>Object</code>.
     * </p>
     * 
     * @param object
     *            the class to get the package name for, may be null
     * @param valueIfNull
     *            the value to return if null
     * @return the package name of the object, or the null value
     */
    public static String getPackageName(Object object, String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getPackageName(object.getClass());
    }

    /**
     * <p>
     * Gets the package name of a <code>Class</code>.
     * </p>
     * 
     * @param cls
     *            the class to get the package name for, may be
     *            <code>null</code>.
     * @return the package name or an empty string
     */
    public static String getPackageName(Class<?> cls) {
        if (cls == null) {
            return EMPTY_STRING;
        }
        return getPackageName(cls.getName());
    }

    /**
     * <p>
     * Gets the package name from a <code>String</code>.
     * </p>
     * 
     * <p>
     * The string passed in is assumed to be a class name - it is not checked.
     * </p>
     * <p>
     * If the class is unpackaged, return an empty string.
     * </p>
     * 
     * @param className
     *            the className to get the package name for, may be
     *            <code>null</code>
     * @return the package name or an empty string
     */
    public static String getPackageName(String className) {
        if (className == null || className.length() == 0) {
            return EMPTY_STRING;
        }

        // Strip array encoding
        while (className.charAt(0) == '[') {
            className = className.substring(1);
        }
        // Strip Object type encoding
        if (className.charAt(0) == 'L'
                && className.charAt(className.length() - 1) == ';') {
            className = className.substring(1);
        }

        int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
        if (i == -1) {
            return EMPTY_STRING;
        }
        return className.substring(0, i);
    }

    // Superclasses/Superinterfaces
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Gets a <code>List</code> of superclasses for the given class.
     * </p>
     * 
     * @param cls
     *            the class to look up, may be <code>null</code>
     * @return the <code>List</code> of superclasses in order going up from this
     *         one <code>null</code> if null input
     */
    public static List<Class<?>> getAllSuperclasses(Class<?> cls) {
        if (cls == null) {
            return null;
        }
        List<Class<?>> classes = new ArrayList<Class<?>>();
        Class<?> superclass = cls.getSuperclass();
        while (superclass != null) {
            classes.add(superclass);
            superclass = superclass.getSuperclass();
        }
        return classes;
    }

    /**
     * <p>
     * Gets a <code>List</code> of all interfaces implemented by the given class
     * and its superclasses.
     * </p>
     * 
     * <p>
     * The order is determined by looking through each interface in turn as
     * declared in the source file and following its hierarchy up. Then each
     * superclass is considered in the same way. Later duplicates are ignored,
     * so the order is maintained.
     * </p>
     * 
     * @param cls
     *            the class to look up, may be <code>null</code>
     * @return the <code>List</code> of interfaces in order, <code>null</code>
     *         if null input
     */
    public static List<Class<?>> getAllInterfaces(Class<?> cls) {
        if (cls == null) {
            return null;
        }

        LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();
        getAllInterfaces(cls, interfacesFound);

        return new ArrayList<Class<?>>(interfacesFound);
    }

    /**
     * Get the interfaces for the specified class.
     * 
     * @param cls
     *            the class to look up, may be <code>null</code>
     * @param interfacesFound
     *            the <code>Set</code> of interfaces for the class
     */
    private static void getAllInterfaces(Class<?> cls,
            HashSet<Class<?>> interfacesFound) {
        while (cls != null) {
            Class<?>[] interfaces = cls.getInterfaces();

            for (Class<?> i : interfaces) {
                if (interfacesFound.add(i)) {
                    getAllInterfaces(i, interfacesFound);
                }
            }

            cls = cls.getSuperclass();
        }
    }

    // Convert list
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Given a <code>List</code> of class names, this method converts them into
     * classes.
     * </p>
     * 
     * <p>
     * A new <code>List</code> is returned. If the class name cannot be found,
     * <code>null</code> is stored in the <code>List</code>. If the class name
     * in the <code>List</code> is <code>null</code>, <code>null</code> is
     * stored in the output <code>List</code>.
     * </p>
     * 
     * @param classNames
     *            the classNames to change
     * @return a <code>List</code> of Class objects corresponding to the class
     *         names, <code>null</code> if null input
     * @throws ClassCastException
     *             if classNames contains a non String entry
     */
    public static List<Class<?>> convertClassNamesToClasses(
            List<String> classNames) {
        if (classNames == null) {
            return null;
        }
        List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size());
        for (String className : classNames) {
            try {
                classes.add(Class.forName(className));
            } catch (Exception ex) {
                classes.add(null);
            }
        }
        return classes;
    }

    /**
     * <p>
     * Given a <code>List</code> of <code>Class</code> objects, this method
     * converts them into class names.
     * </p>
     * 
     * <p>
     * A new <code>List</code> is returned. <code>null</code> objects will be
     * copied into the returned list as <code>null</code>.
     * </p>
     * 
     * @param classes
     *            the classes to change
     * @return a <code>List</code> of class names corresponding to the Class
     *         objects, <code>null</code> if null input
     * @throws ClassCastException
     *             if <code>classes</code> contains a non-<code>Class</code>
     *             entry
     */
    public static List<String> convertClassesToClassNames(List<Class<?>> classes) {
        if (classes == null) {
            return null;
        }
        List<String> classNames = new ArrayList<String>(classes.size());
        for (Class<?> cls : classes) {
            if (cls == null) {
                classNames.add(null);
            } else {
                classNames.add(cls.getName());
            }
        }
        return classNames;
    }

    // Is assignable
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Checks if an array of Classes can be assigned to another array of
     * Classes.
     * </p>
     * 
     * <p>
     * This method calls {@link #isAssignable(Class, Class) isAssignable} for
     * each Class pair in the input arrays. It can be used to check if a set of
     * arguments (the first parameter) are suitably compatible with a set of
     * method parameter types (the second parameter).
     * </p>
     * 
     * <p>
     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
     * method takes into account widenings of primitive classes and
     * <code>null</code>s.
     * </p>
     * 
     * <p>
     * Primitive widenings allow an int to be assigned to a <code>long</code>,
     * <code>float</code> or <code>double</code>. This method returns the
     * correct result for these cases.
     * </p>
     * 
     * <p>
     * <code>Null</code> may be assigned to any reference type. This method will
     * return <code>true</code> if <code>null</code> is passed in and the
     * toClass is non-primitive.
     * </p>
     * 
     * <p>
     * Specifically, this method tests whether the type represented by the
     * specified <code>Class</code> parameter can be converted to the type
     * represented by this <code>Class</code> object via an identity conversion
     * widening primitive or widening reference conversion. See
     * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>
     * , sections 5.1.1, 5.1.2 and 5.1.4 for details.
     * </p>
     * 
     * <p>
     * <strong>Since Lang 3.0,</strong> this method will default behavior for
     * calculating assignability between primitive and wrapper types
     * <em>corresponding
     * to the running Java version</em>; i.e. autoboxing will be the default
     * behavior in VMs running Java versions >= 1.5.
     * </p>
     * 
     * @param classArray
     *            the array of Classes to check, may be <code>null</code>
     * @param toClassArray
     *            the array of Classes to try to assign into, may be
     *            <code>null</code>
     * @return <code>true</code> if assignment possible
     */
    public static boolean isAssignable(Class<?>[] classArray,
            Class<?>[] toClassArray) {
        return isAssignable(classArray, toClassArray,
                isJavaVersionAtLeast(1.5f));
    }

    /**
     * <p>
     * Checks if an array of Classes can be assigned to another array of
     * Classes.
     * </p>
     * 
     * <p>
     * This method calls {@link #isAssignable(Class, Class) isAssignable} for
     * each Class pair in the input arrays. It can be used to check if a set of
     * arguments (the first parameter) are suitably compatible with a set of
     * method parameter types (the second parameter).
     * </p>
     * 
     * <p>
     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
     * method takes into account widenings of primitive classes and
     * <code>null</code>s.
     * </p>
     * 
     * <p>
     * Primitive widenings allow an int to be assigned to a <code>long</code>,
     * <code>float</code> or <code>double</code>. This method returns the
     * correct result for these cases.
     * </p>
     * 
     * <p>
     * <code>Null</code> may be assigned to any reference type. This method will
     * return <code>true</code> if <code>null</code> is passed in and the
     * toClass is non-primitive.
     * </p>
     * 
     * <p>
     * Specifically, this method tests whether the type represented by the
     * specified <code>Class</code> parameter can be converted to the type
     * represented by this <code>Class</code> object via an identity conversion
     * widening primitive or widening reference conversion. See
     * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>
     * , sections 5.1.1, 5.1.2 and 5.1.4 for details.
     * </p>
     * 
     * @param classArray
     *            the array of Classes to check, may be <code>null</code>
     * @param toClassArray
     *            the array of Classes to try to assign into, may be
     *            <code>null</code>
     * @param autoboxing
     *            whether to use implicit autoboxing/unboxing between primitives
     *            and wrappers
     * @return <code>true</code> if assignment possible
     */
    public static boolean isAssignable(Class<?>[] classArray,
            Class<?>[] toClassArray, boolean autoboxing) {
        if (isSameLength(classArray, toClassArray) == false) {
            return false;
        }
        if (classArray == null) {
            classArray = EMPTY_CLASS_ARRAY;
        }
        if (toClassArray == null) {
            toClassArray = EMPTY_CLASS_ARRAY;
        }
        for (int i = 0; i < classArray.length; i++) {
            if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks if one <code>Class</code> can be assigned to a variable of another
     * <code>Class</code>.
     * </p>
     * 
     * <p>
     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
     * method takes into account widenings of primitive classes and
     * <code>null</code>s.
     * </p>
     * 
     * <p>
     * Primitive widenings allow an int to be assigned to a long, float or
     * double. This method returns the correct result for these cases.
     * </p>
     * 
     * <p>
     * <code>Null</code> may be assigned to any reference type. This method will
     * return <code>true</code> if <code>null</code> is passed in and the
     * toClass is non-primitive.
     * </p>
     * 
     * <p>
     * Specifically, this method tests whether the type represented by the
     * specified <code>Class</code> parameter can be converted to the type
     * represented by this <code>Class</code> object via an identity conversion
     * widening primitive or widening reference conversion. See
     * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>
     * , sections 5.1.1, 5.1.2 and 5.1.4 for details.
     * </p>
     * 
     * <p>
     * <strong>Since Lang 3.0,</strong> this method will default behavior for
     * calculating assignability between primitive and wrapper types
     * <em>corresponding
     * to the running Java version</em>; i.e. autoboxing will be the default
     * behavior in VMs running Java versions >= 1.5.
     * </p>
     * 
     * @param cls
     *            the Class to check, may be null
     * @param toClass
     *            the Class to try to assign into, returns false if null
     * @return <code>true</code> if assignment possible
     */
    public static boolean isAssignable(Class<?> cls, Class<?> toClass) {
        return isAssignable(cls, toClass, isJavaVersionAtLeast(1.5f));
    }

    /**
     * <p>
     * Checks if one <code>Class</code> can be assigned to a variable of another
     * <code>Class</code>.
     * </p>
     * 
     * <p>
     * Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
     * method takes into account widenings of primitive classes and
     * <code>null</code>s.
     * </p>
     * 
     * <p>
     * Primitive widenings allow an int to be assigned to a long, float or
     * double. This method returns the correct result for these cases.
     * </p>
     * 
     * <p>
     * <code>Null</code> may be assigned to any reference type. This method will
     * return <code>true</code> if <code>null</code> is passed in and the
     * toClass is non-primitive.
     * </p>
     * 
     * <p>
     * Specifically, this method tests whether the type represented by the
     * specified <code>Class</code> parameter can be converted to the type
     * represented by this <code>Class</code> object via an identity conversion
     * widening primitive or widening reference conversion. See
     * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>
     * , sections 5.1.1, 5.1.2 and 5.1.4 for details.
     * </p>
     * 
     * @param cls
     *            the Class to check, may be null
     * @param toClass
     *            the Class to try to assign into, returns false if null
     * @param autoboxing
     *            whether to use implicit autoboxing/unboxing between primitives
     *            and wrappers
     * @return <code>true</code> if assignment possible
     */
    public static boolean isAssignable(Class<?> cls, Class<?> toClass,
            boolean autoboxing) {
        if (toClass == null) {
            return false;
        }
        // have to check for null, as isAssignableFrom doesn't
        if (cls == null) {
            return !(toClass.isPrimitive());
        }
        // autoboxing:
        if (autoboxing) {
            if (cls.isPrimitive() && !toClass.isPrimitive()) {
                cls = primitiveToWrapper(cls);
                if (cls == null) {
                    return false;
                }
            }
            if (toClass.isPrimitive() && !cls.isPrimitive()) {
                cls = wrapperToPrimitive(cls);
                if (cls == null) {
                    return false;
                }
            }
        }
        if (cls.equals(toClass)) {
            return true;
        }
        if (cls.isPrimitive()) {
            if (toClass.isPrimitive() == false) {
                return false;
            }
            if (Integer.TYPE.equals(cls)) {
                return Long.TYPE.equals(toClass) || Float.TYPE.equals(toClass)
                        || Double.TYPE.equals(toClass);
            }
            if (Long.TYPE.equals(cls)) {
                return Float.TYPE.equals(toClass)
                        || Double.TYPE.equals(toClass);
            }
            if (Boolean.TYPE.equals(cls)) {
                return false;
            }
            if (Double.TYPE.equals(cls)) {
                return false;
            }
            if (Float.TYPE.equals(cls)) {
                return Double.TYPE.equals(toClass);
            }
            if (Character.TYPE.equals(cls)) {
                return Integer.TYPE.equals(toClass)
                        || Long.TYPE.equals(toClass)
                        || Float.TYPE.equals(toClass)
                        || Double.TYPE.equals(toClass);
            }
            if (Short.TYPE.equals(cls)) {
                return Integer.TYPE.equals(toClass)
                        || Long.TYPE.equals(toClass)
                        || Float.TYPE.equals(toClass)
                        || Double.TYPE.equals(toClass);
            }
            if (Byte.TYPE.equals(cls)) {
                return Short.TYPE.equals(toClass)
                        || Integer.TYPE.equals(toClass)
                        || Long.TYPE.equals(toClass)
                        || Float.TYPE.equals(toClass)
                        || Double.TYPE.equals(toClass);
            }
            // should never get here
            return false;
        }
        return toClass.isAssignableFrom(cls);
    }

    /**
     * <p>
     * Converts the specified primitive Class object to its corresponding
     * wrapper Class object.
     * </p>
     * 
     * <p>
     * NOTE: From v2.2, this method handles <code>Void.TYPE</code>, returning
     * <code>Void.TYPE</code>.
     * </p>
     * 
     * @param cls
     *            the class to convert, may be null
     * @return the wrapper class for <code>cls</code> or <code>cls</code> if
     *         <code>cls</code> is not a primitive. <code>null</code> if null
     *         input.
     * @since 2.1
     */
    public static Class<?> primitiveToWrapper(Class<?> cls) {
        Class<?> convertedClass = cls;
        if (cls != null && cls.isPrimitive()) {
            convertedClass = primitiveWrapperMap.get(cls);
        }
        return convertedClass;
    }

    /**
     * <p>
     * Converts the specified array of primitive Class objects to an array of
     * its corresponding wrapper Class objects.
     * </p>
     * 
     * @param classes
     *            the class array to convert, may be null or empty
     * @return an array which contains for each given class, the wrapper class
     *         or the original class if class is not a primitive.
     *         <code>null</code> if null input. Empty array if an empty array
     *         passed in.
     * @since 2.1
     */
    public static Class<?>[] primitivesToWrappers(Class<?>[] classes) {
        if (classes == null) {
            return null;
        }

        if (classes.length == 0) {
            return classes;
        }

        Class<?>[] convertedClasses = new Class[classes.length];
        for (int i = 0; i < classes.length; i++) {
            convertedClasses[i] = primitiveToWrapper(classes[i]);
        }
        return convertedClasses;
    }

    /**
     * <p>
     * Converts the specified wrapper class to its corresponding primitive
     * class.
     * </p>
     * 
     * <p>
     * This method is the counter part of <code>primitiveToWrapper()</code>. If
     * the passed in class is a wrapper class for a primitive type, this
     * primitive type will be returned (e.g. <code>Integer.TYPE</code> for
     * <code>Integer.class</code>). For other classes, or if the parameter is
     * <b>null</b>, the return value is <b>null</b>.
     * </p>
     * 
     * @param cls
     *            the class to convert, may be <b>null</b>
     * @return the corresponding primitive type if <code>cls</code> is a wrapper
     *         class, <b>null</b> otherwise
     * @see #primitiveToWrapper(Class)
     * @since 2.4
     */
    public static Class<?> wrapperToPrimitive(Class<?> cls) {
        return wrapperPrimitiveMap.get(cls);
    }

    /**
     * <p>
     * Converts the specified array of wrapper Class objects to an array of its
     * corresponding primitive Class objects.
     * </p>
     * 
     * <p>
     * This method invokes <code>wrapperToPrimitive()</code> for each element of
     * the passed in array.
     * </p>
     * 
     * @param classes
     *            the class array to convert, may be null or empty
     * @return an array which contains for each given class, the primitive class
     *         or <b>null</b> if the original class is not a wrapper class.
     *         <code>null</code> if null input. Empty array if an empty array
     *         passed in.
     * @see #wrapperToPrimitive(Class)
     * @since 2.4
     */
    public static Class<?>[] wrappersToPrimitives(Class<?>[] classes) {
        if (classes == null) {
            return null;
        }

        if (classes.length == 0) {
            return classes;
        }

        Class<?>[] convertedClasses = new Class[classes.length];
        for (int i = 0; i < classes.length; i++) {
            convertedClasses[i] = wrapperToPrimitive(classes[i]);
        }
        return convertedClasses;
    }

    // Inner class
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Is the specified class an inner class or static nested class.
     * </p>
     * 
     * @param cls
     *            the class to check, may be null
     * @return <code>true</code> if the class is an inner or static nested
     *         class, false if not or <code>null</code>
     */
    public static boolean isInnerClass(Class<?> cls) {
        if (cls == null) {
            return false;
        }
        return cls.getName().indexOf(INNER_CLASS_SEPARATOR_CHAR) >= 0;
    }

    // Class loading
    // ----------------------------------------------------------------------
    /**
     * Returns the class represented by <code>className</code> using the
     * <code>classLoader</code>. This implementation supports the syntaxes "
     * <code>java.util.Map.Entry[]</code>", "<code>java.util.Map$Entry[]</code>
     * ", "<code>[Ljava.util.Map.Entry;</code>", and "
     * <code>[Ljava.util.Map$Entry;</code>".
     * 
     * @param classLoader
     *            the class loader to use to load the class
     * @param className
     *            the class name
     * @param initialize
     *            whether the class must be initialized
     * @return the class represented by <code>className</code> using the
     *         <code>classLoader</code>
     * @throws ClassNotFoundException
     *             if the class is not found
     */
    public static Class<?> getClass(ClassLoader classLoader, String className,
            boolean initialize) throws ClassNotFoundException {
        try {
            Class<?> clazz;
            if (abbreviationMap.containsKey(className)) {
                String clsName = "[" + abbreviationMap.get(className);
                clazz = Class.forName(clsName, initialize, classLoader)
                        .getComponentType();
            } else {
                clazz = Class.forName(toCanonicalName(className), initialize,
                        classLoader);
            }
            return clazz;
        } catch (ClassNotFoundException ex) {
            // allow path separators (.) as inner class name separators
            int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);

            if (lastDotIndex != -1) {
                try {
                    return getClass(
                            classLoader,
                            className.substring(0, lastDotIndex)
                                    + INNER_CLASS_SEPARATOR_CHAR
                                    + className.substring(lastDotIndex + 1),
                            initialize);
                } catch (ClassNotFoundException ex2) {
                }
            }

            throw ex;
        }
    }

    /**
     * Returns the (initialized) class represented by <code>className</code>
     * using the <code>classLoader</code>. This implementation supports the
     * syntaxes "<code>java.util.Map.Entry[]</code>", "
     * <code>java.util.Map$Entry[]</code>", "<code>[Ljava.util.Map.Entry;</code>
     * ", and "<code>[Ljava.util.Map$Entry;</code>".
     * 
     * @param classLoader
     *            the class loader to use to load the class
     * @param className
     *            the class name
     * @return the class represented by <code>className</code> using the
     *         <code>classLoader</code>
     * @throws ClassNotFoundException
     *             if the class is not found
     */
    public static Class<?> getClass(ClassLoader classLoader, String className)
            throws ClassNotFoundException {
        return getClass(classLoader, className, true);
    }

    /**
     * Returns the (initialized) class represented by <code>className</code>
     * using the current thread's context class loader. This implementation
     * supports the syntaxes "<code>java.util.Map.Entry[]</code>", "
     * <code>java.util.Map$Entry[]</code>", "<code>[Ljava.util.Map.Entry;</code>
     * ", and "<code>[Ljava.util.Map$Entry;</code>".
     * 
     * @param className
     *            the class name
     * @return the class represented by <code>className</code> using the current
     *         thread's context class loader
     * @throws ClassNotFoundException
     *             if the class is not found
     */
    public static Class<?> getClass(String className)
            throws ClassNotFoundException {
        return getClass(className, true);
    }

    /**
     * Returns the class represented by <code>className</code> using the current
     * thread's context class loader. This implementation supports the syntaxes
     * "<code>java.util.Map.Entry[]</code>", "<code>java.util.Map$Entry[]</code>
     * ", "<code>[Ljava.util.Map.Entry;</code>", and "
     * <code>[Ljava.util.Map$Entry;</code>".
     * 
     * @param className
     *            the class name
     * @param initialize
     *            whether the class must be initialized
     * @return the class represented by <code>className</code> using the current
     *         thread's context class loader
     * @throws ClassNotFoundException
     *             if the class is not found
     */
    public static Class<?> getClass(String className, boolean initialize)
            throws ClassNotFoundException {
        ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
        ClassLoader loader = contextCL == null ? Functions.class
                .getClassLoader() : contextCL;
        return getClass(loader, className, initialize);
    }

    // Public method
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Returns the desired Method much like <code>Class.getMethod</code>,
     * however it ensures that the returned Method is from a public class or
     * interface and not from an anonymous inner class. This means that the
     * Method is invokable and doesn't fall foul of Java bug <a
     * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957"
     * >4071957</a>).
     * 
     * <code><pre>Set set = Collections.unmodifiableSet(...);
     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
     *  Object result = method.invoke(set, new Object[]);</pre></code>
     * </p>
     * 
     * @param cls
     *            the class to check, not null
     * @param methodName
     *            the name of the method
     * @param parameterTypes
     *            the list of parameters
     * @return the method
     * @throws NullPointerException
     *             if the class is null
     * @throws SecurityException
     *             if a a security violation occured
     * @throws NoSuchMethodException
     *             if the method is not found in the given class or if the
     *             metothod doen't conform with the requirements
     */
    public static Method getPublicMethod(Class<?> cls, String methodName,
            Class<?> parameterTypes[]) throws SecurityException,
            NoSuchMethodException {

        Method declaredMethod = cls.getMethod(methodName, parameterTypes);
        if (Modifier
                .isPublic(declaredMethod.getDeclaringClass().getModifiers())) {
            return declaredMethod;
        }

        List<Class<?>> candidateClasses = new ArrayList<Class<?>>();
        candidateClasses.addAll(getAllInterfaces(cls));
        candidateClasses.addAll(getAllSuperclasses(cls));

        for (Class<?> candidateClass : candidateClasses) {
            if (!Modifier.isPublic(candidateClass.getModifiers())) {
                continue;
            }
            Method candidateMethod;
            try {
                candidateMethod = candidateClass.getMethod(methodName,
                        parameterTypes);
            } catch (NoSuchMethodException ex) {
                continue;
            }
            if (Modifier.isPublic(candidateMethod.getDeclaringClass()
                    .getModifiers())) {
                return candidateMethod;
            }
        }

        throw new NoSuchMethodException("Can't find a public method for "
                + methodName);
    }

    // ----------------------------------------------------------------------
    /**
     * Converts a class name to a JLS style class name.
     * 
     * @param className
     *            the class name
     * @return the converted name
     */
    private static String toCanonicalName(String className) {
        className = deleteWhitespace(className);
        if (className == null) {
            throw new NullPointerException("className must not be null.");
        } else if (className.endsWith("[]")) {
            StringBuilder classNameBuffer = new StringBuilder();
            while (className.endsWith("[]")) {
                className = className.substring(0, className.length() - 2);
                classNameBuffer.append("[");
            }
            String abbreviation = abbreviationMap.get(className);
            if (abbreviation != null) {
                classNameBuffer.append(abbreviation);
            } else {
                classNameBuffer.append("L").append(className).append(";");
            }
            className = classNameBuffer.toString();
        }
        return className;
    }

    /**
     * <p>
     * Converts an array of <code>Object</code> in to an array of
     * <code>Class</code> objects. If any of these objects is null, a null
     * element will be inserted into the array.
     * </p>
     * 
     * <p>
     * This method returns <code>null</code> for a <code>null</code> input
     * array.
     * </p>
     * 
     * @param array
     *            an <code>Object</code> array
     * @return a <code>Class</code> array, <code>null</code> if null array input
     * @since 2.4
     */
    public static Class<?>[] toClass(Object[] array) {
        if (array == null) {
            return null;
        } else if (array.length == 0) {
            return EMPTY_CLASS_ARRAY;
        }
        Class<?>[] classes = new Class[array.length];
        for (int i = 0; i < array.length; i++) {
            classes[i] = array[i] == null ? null : array[i].getClass();
        }
        return classes;
    }

    // Short canonical name
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Gets the canonical name minus the package name for an <code>Object</code>
     * .
     * </p>
     * 
     * @param object
     *            the class to get the short name for, may be null
     * @param valueIfNull
     *            the value to return if null
     * @return the canonical name of the object without the package name, or the
     *         null value
     * @since 2.4
     */
    public static String getShortCanonicalName(Object object, String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getShortCanonicalName(object.getClass().getName());
    }

    /**
     * <p>
     * Gets the canonical name minus the package name from a <code>Class</code>.
     * </p>
     * 
     * @param cls
     *            the class to get the short name for.
     * @return the canonical name without the package name or an empty string
     * @since 2.4
     */
    public static String getShortCanonicalName(Class<?> cls) {
        if (cls == null) {
            return EMPTY_STRING;
        }
        return getShortCanonicalName(cls.getName());
    }

    /**
     * <p>
     * Gets the canonical name minus the package name from a String.
     * </p>
     * 
     * <p>
     * The string passed in is assumed to be a canonical name - it is not
     * checked.
     * </p>
     * 
     * @param canonicalName
     *            the class name to get the short name for
     * @return the canonical name of the class without the package name or an
     *         empty string
     * @since 2.4
     */
    public static String getShortCanonicalName(String canonicalName) {
        return getShortClassName(getCanonicalName(canonicalName));
    }

    // Package name
    // ----------------------------------------------------------------------
    /**
     * <p>
     * Gets the package name from the canonical name of an <code>Object</code>.
     * </p>
     * 
     * @param object
     *            the class to get the package name for, may be null
     * @param valueIfNull
     *            the value to return if null
     * @return the package name of the object, or the null value
     * @since 2.4
     */
    public static String getPackageCanonicalName(Object object,
            String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getPackageCanonicalName(object.getClass().getName());
    }

    /**
     * <p>
     * Gets the package name from the canonical name of a <code>Class</code>.
     * </p>
     * 
     * @param cls
     *            the class to get the package name for, may be
     *            <code>null</code>.
     * @return the package name or an empty string
     * @since 2.4
     */
    public static String getPackageCanonicalName(Class<?> cls) {
        if (cls == null) {
            return EMPTY_STRING;
        }
        return getPackageCanonicalName(cls.getName());
    }

    /**
     * <p>
     * Gets the package name from the canonical name.
     * </p>
     * 
     * <p>
     * The string passed in is assumed to be a canonical name - it is not
     * checked.
     * </p>
     * <p>
     * If the class is unpackaged, return an empty string.
     * </p>
     * 
     * @param canonicalName
     *            the canonical name to get the package name for, may be
     *            <code>null</code>
     * @return the package name or an empty string
     * @since 2.4
     */
    public static String getPackageCanonicalName(String canonicalName) {
        return getPackageName(getCanonicalName(canonicalName));
    }

    /**
     * <p>
     * Converts a given name of class into canonical format. If name of class is
     * not a name of array class it returns unchanged name.
     * </p>
     * <p>
     * Example:
     * <ul>
     * <li><code>getCanonicalName("[I") = "int[]"</code></li>
     * <li>
     * <code>getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"</code>
     * </li>
     * <li>
     * <code>getCanonicalName("java.lang.String") = "java.lang.String"</code></li>
     * </ul>
     * </p>
     * 
     * @param className
     *            the name of class
     * @return canonical form of class name
     * @since 2.4
     */
    private static String getCanonicalName(String className) {
        className = deleteWhitespace(className);
        if (className == null) {
            return null;
        } else {
            int dim = 0;
            while (className.startsWith("[")) {
                dim++;
                className = className.substring(1);
            }
            if (dim < 1) {
                return className;
            } else {
                if (className.startsWith("L")) {
                    className = className.substring(1,
                            className.endsWith(";") ? className.length() - 1
                                    : className.length());
                } else {
                    if (className.length() > 0) {
                        className = reverseAbbreviationMap.get(className
                                .substring(0, 1));
                    }
                }
                StringBuilder canonicalClassNameBuffer = new StringBuilder(
                        className);
                for (int i = 0; i < dim; i++) {
                    canonicalClassNameBuffer.append("[]");
                }
                return canonicalClassNameBuffer.toString();
            }
        }
    }

    // ///////////////////////////////////////////
    //
    // CharEncoding
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * ISO Latin Alphabet #1, also known as ISO-LATIN-1.
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String ISO_8859_1 = "ISO-8859-1";

    /**
     * <p>
     * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin
     * block of the Unicode character set.
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String US_ASCII = "US-ASCII";

    /**
     * <p>
     * Sixteen-bit Unicode Transformation Format, byte order specified by a
     * mandatory initial byte-order mark (either order accepted on input,
     * big-endian used on output).
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String UTF_16 = "UTF-16";

    /**
     * <p>
     * Sixteen-bit Unicode Transformation Format, big-endian byte order.
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String UTF_16BE = "UTF-16BE";

    /**
     * <p>
     * Sixteen-bit Unicode Transformation Format, little-endian byte order.
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String UTF_16LE = "UTF-16LE";

    /**
     * <p>
     * Eight-bit Unicode Transformation Format.
     * </p>
     * <p>
     * Every implementation of the Java platform is required to support this
     * character encoding.
     * </p>
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static final String UTF_8 = "UTF-8";

    /**
     * <p>
     * Returns whether the named charset is supported.
     * </p>
     * <p>
     * This is similar to <a href=
     * "http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/Charset.html#isSupported(java.lang.String)"
     * > java.nio.charset.Charset.isSupported(String)</a>
     * </p>
     * 
     * @param name
     *            the name of the requested charset; may be either a canonical
     *            name or an alias
     * @return <code>true</code> if, and only if, support for the named charset
     *         is available in the current Java virtual machine
     * 
     * @see <a
     *      href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE
     *      character encoding names</a>
     */
    public static boolean isSupported(String name) {
        if (name == null) {
            return false;
        }
        try {
            new String(EMPTY_BYTE_ARRAY, name);
        } catch (UnsupportedEncodingException e) {
            return false;
        }
        return true;
    }

    // ///////////////////////////////////////////
    //
    // EnumUtils
    //
    // ///////////////////////////////////////////

    /**
     * Gets the <code>Map</code> of <code>enums</code> by name.
     * <p>
     * This method is useful when you need a map of enums by name.
     * 
     * @param enumClass
     *            the class of the <code>enum</code> to get, not null
     * @return the modifiable map of enum names to enums, never null
     */
    public static <E extends Enum<E>> Map<String, E> getEnumMap(
            Class<E> enumClass) {
        Map<String, E> map = new LinkedHashMap<String, E>();
        for (E e : enumClass.getEnumConstants()) {
            map.put(e.name(), e);
        }
        return map;
    }

    /**
     * Gets the <code>List</code> of <code>enums</code>.
     * <p>
     * This method is useful when you need a list of enums rather than an array.
     * 
     * @param enumClass
     *            the class of the <code>enum</code> to get, not null
     * @return the modifiable list of enums, never null
     */
    public static <E extends Enum<E>> List<E> getEnumList(Class<E> enumClass) {
        return new ArrayList<E>(Arrays.asList(enumClass.getEnumConstants()));
    }

    /**
     * Checks if the specified name is a valid <code>enum</code> for the class.
     * <p>
     * This method differs from {@link Enum#valueOf} in that checks if the name
     * is a valid enum without needing to catch the exception.
     * 
     * @param enumClass
     *            the class of the <code>enum</code> to get, not null
     * @param enumName
     *            the enum name
     * @return true if the enum name is valid, otherwise false
     */
    public static <E extends Enum<E>> boolean isValidEnum(Class<E> enumClass,
            String enumName) {
        try {
            Enum.valueOf(enumClass, enumName);
            return true;
        } catch (IllegalArgumentException ex) {
            return false;
        }
    }

    /**
     * Gets the <code>enum</code> for the class, returning <code>null</code> if
     * not found.
     * <p>
     * This method differs from {@link Enum#valueOf} in that it does not throw
     * an exception for an invalid enum name.
     * 
     * @param enumClass
     *            the class of the <code>enum</code> to get, not null
     * @param enumName
     *            the enum name
     * @return the enum or null if not found
     */
    public static <E extends Enum<E>> E getEnum(Class<E> enumClass,
            String enumName) {
        try {
            return Enum.valueOf(enumClass, enumName);
        } catch (IllegalArgumentException ex) {
            return null;
        }
    }

    // ///////////////////////////////////////////
    //
    // LocaleUtils
    //
    // ///////////////////////////////////////////

    /** Unmodifiable list of available locales. */
    // @GuardedBy("this")
    private static List<Locale> cAvailableLocaleList; // lazily created by
    // availableLocaleList()

    /** Unmodifiable set of available locales. */
    // @GuardedBy("this")
    private static Set<Locale> cAvailableLocaleSet; // lazily created by
    // availableLocaleSet()

    /** Unmodifiable map of language locales by country. */
    private static final Map<String, List<Locale>> cLanguagesByCountry = Collections
            .synchronizedMap(new HashMap<String, List<Locale>>());

    /** Unmodifiable map of country locales by language. */
    private static final Map<String, List<Locale>> cCountriesByLanguage = Collections
            .synchronizedMap(new HashMap<String, List<Locale>>());

    /**
     * <p>
     * Converts a String to a Locale.
     * </p>
     * 
     * <p>
     * This method takes the string format of a locale and creates the locale
     * object from it.
     * </p>
     * 
     * <pre>
     *   LocaleUtils.toLocale("en")         = new Locale("en", "")
     *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
     *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
     * </pre>
     * 
     * <p>
     * (#) The behaviour of the JDK variant constructor changed between JDK1.3
     * and JDK1.4. In JDK1.3, the constructor upper cases the variant, in
     * JDK1.4, it doesn't. Thus, the result from getVariant() may vary depending
     * on your JDK.
     * </p>
     * 
     * <p>
     * This method validates the input strictly. The language code must be
     * lowercase. The country code must be uppercase. The separator must be an
     * underscore. The length must be correct.
     * </p>
     * 
     * @param str
     *            the locale String to convert, null returns null
     * @return a Locale, null if null input
     * @throws IllegalArgumentException
     *             if the string is an invalid format
     */
    public static Locale toLocale(String str) {
        if (str == null) {
            return null;
        }
        int len = str.length();
        if (len != 2 && len != 5 && len < 7) {
            throw new IllegalArgumentException("Invalid locale format: " + str);
        }
        char ch0 = str.charAt(0);
        char ch1 = str.charAt(1);
        if (ch0 < 'a' || ch0 > 'z' || ch1 < 'a' || ch1 > 'z') {
            throw new IllegalArgumentException("Invalid locale format: " + str);
        }
        if (len == 2) {
            return new Locale(str, "");
        } else {
            if (str.charAt(2) != '_') {
                throw new IllegalArgumentException("Invalid locale format: "
                        + str);
            }
            char ch3 = str.charAt(3);
            if (ch3 == '_') {
                return new Locale(str.substring(0, 2), "", str.substring(4));
            }
            char ch4 = str.charAt(4);
            if (ch3 < 'A' || ch3 > 'Z' || ch4 < 'A' || ch4 > 'Z') {
                throw new IllegalArgumentException("Invalid locale format: "
                        + str);
            }
            if (len == 5) {
                return new Locale(str.substring(0, 2), str.substring(3, 5));
            } else {
                if (str.charAt(5) != '_') {
                    throw new IllegalArgumentException(
                            "Invalid locale format: " + str);
                }
                return new Locale(str.substring(0, 2), str.substring(3, 5),
                        str.substring(6));
            }
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains the list of locales to search through when performing a locale
     * search.
     * </p>
     * 
     * <pre>
     * localeLookupList(Locale("fr","CA","xxx"))
     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr")]
     * </pre>
     * 
     * @param locale
     *            the locale to start from
     * @return the unmodifiable list of Locale objects, 0 being locale, never
     *         null
     */
    public static List<Locale> localeLookupList(Locale locale) {
        return localeLookupList(locale, locale);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains the list of locales to search through when performing a locale
     * search.
     * </p>
     * 
     * <pre>
     * localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr"), Locale("en"]
     * </pre>
     * 
     * <p>
     * The result list begins with the most specific locale, then the next more
     * general and so on, finishing with the default locale. The list will never
     * contain the same locale twice.
     * </p>
     * 
     * @param locale
     *            the locale to start from, null returns empty list
     * @param defaultLocale
     *            the default locale to use if no other is found
     * @return the unmodifiable list of Locale objects, 0 being locale, never
     *         null
     */
    public static List<Locale> localeLookupList(Locale locale,
            Locale defaultLocale) {
        List<Locale> list = new ArrayList<Locale>(4);
        if (locale != null) {
            list.add(locale);
            if (locale.getVariant().length() > 0) {
                list.add(new Locale(locale.getLanguage(), locale.getCountry()));
            }
            if (locale.getCountry().length() > 0) {
                list.add(new Locale(locale.getLanguage(), ""));
            }
            if (list.contains(defaultLocale) == false) {
                list.add(defaultLocale);
            }
        }
        return Collections.unmodifiableList(list);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains an unmodifiable list of installed locales.
     * </p>
     * 
     * <p>
     * This method is a wrapper around {@link Locale#getAvailableLocales()}. It
     * is more efficient, as the JDK method must create a new array each time it
     * is called.
     * </p>
     * 
     * @return the unmodifiable list of available locales
     */
    public static List<Locale> availableLocaleList() {
        if (cAvailableLocaleList == null) {
            initAvailableLocaleList();
        }
        return cAvailableLocaleList;
    }

    /**
     * Initializes the availableLocaleList. It is separate from
     * availableLocaleList() to avoid the synchronized block affecting normal
     * use, yet synchronized and lazy loading to avoid a static block affecting
     * other methods in this class.
     */
    private static synchronized void initAvailableLocaleList() {
        if (cAvailableLocaleList == null) {
            List<Locale> list = Arrays.asList(Locale.getAvailableLocales());
            cAvailableLocaleList = Collections.unmodifiableList(list);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains an unmodifiable set of installed locales.
     * </p>
     * 
     * <p>
     * This method is a wrapper around {@link Locale#getAvailableLocales()}. It
     * is more efficient, as the JDK method must create a new array each time it
     * is called.
     * </p>
     * 
     * @return the unmodifiable set of available locales
     */
    public static Set<Locale> availableLocaleSet() {
        if (cAvailableLocaleSet == null) {
            initAvailableLocaleSet();
        }
        return cAvailableLocaleSet;
    }

    /**
     * Initializes the availableLocaleSet. It is separate from
     * availableLocaleSet() to avoid the synchronized block affecting normal
     * use, yet synchronized and lazy loading to avoid a static block affecting
     * other methods in this class.
     */
    private static synchronized void initAvailableLocaleSet() {
        if (cAvailableLocaleSet == null) {
            cAvailableLocaleSet = Collections
                    .unmodifiableSet(new HashSet<Locale>(availableLocaleList()));
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if the locale specified is in the list of available locales.
     * </p>
     * 
     * @param locale
     *            the Locale object to check if it is available
     * @return true if the locale is a known locale
     */
    public static boolean isAvailableLocale(Locale locale) {
        return availableLocaleList().contains(locale);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains the list of languages supported for a given country.
     * </p>
     * 
     * <p>
     * This method takes a country code and searches to find the languages
     * available for that country. Variant locales are removed.
     * </p>
     * 
     * @param countryCode
     *            the 2 letter country code, null returns empty
     * @return an unmodifiable List of Locale objects, never null
     */
    public static List<Locale> languagesByCountry(String countryCode) {
        List<Locale> langs = cLanguagesByCountry.get(countryCode); // syncd
        if (langs == null) {
            if (countryCode != null) {
                langs = new ArrayList<Locale>();
                List<Locale> locales = availableLocaleList();
                for (int i = 0; i < locales.size(); i++) {
                    Locale locale = locales.get(i);
                    if (countryCode.equals(locale.getCountry())
                            && locale.getVariant().length() == 0) {
                        langs.add(locale);
                    }
                }
                langs = Collections.unmodifiableList(langs);
            } else {
                langs = Collections.emptyList();
            }
            cLanguagesByCountry.put(countryCode, langs); // syncd
        }
        return langs;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Obtains the list of countries supported for a given language.
     * </p>
     * 
     * <p>
     * This method takes a language code and searches to find the countries
     * available for that language. Variant locales are removed.
     * </p>
     * 
     * @param languageCode
     *            the 2 letter language code, null returns empty
     * @return an unmodifiable List of Locale objects, never null
     */
    public static List<Locale> countriesByLanguage(String languageCode) {
        List<Locale> countries = cCountriesByLanguage.get(languageCode); // syncd
        if (countries == null) {
            if (languageCode != null) {
                countries = new ArrayList<Locale>();
                List<Locale> locales = availableLocaleList();
                for (int i = 0; i < locales.size(); i++) {
                    Locale locale = locales.get(i);
                    if (languageCode.equals(locale.getLanguage())
                            && locale.getCountry().length() != 0
                            && locale.getVariant().length() == 0) {
                        countries.add(locale);
                    }
                }
                countries = Collections.unmodifiableList(countries);
            } else {
                countries = Collections.emptyList();
            }
            cCountriesByLanguage.put(languageCode, countries); // syncd
        }
        return countries;
    }

    // ///////////////////////////////////////////
    //
    // RandomStringUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Random object used by random method. This has to be not local to the
     * random method so as to not return the same value in the same millisecond.
     * </p>
     */
    private static final Random RANDOM = new Random();

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of all characters.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @return the random string
     */
    public static String random(int count) {
        return random(count, false, false);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of characters whose ASCII value is
     * between <code>32</code> and <code>126</code> (inclusive).
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @return the random string
     */
    public static String randomAscii(int count) {
        return random(count, 32, 127, false, false);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of alphabetic characters.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @return the random string
     */
    public static String randomAlphabetic(int count) {
        return random(count, true, false);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of alpha-numeric characters.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @return the random string
     */
    public static String randomAlphanumeric(int count) {
        return random(count, true, true);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of numeric characters.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @return the random string
     */
    public static String randomNumeric(int count) {
        return random(count, false, true);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of alpha-numeric characters as
     * indicated by the arguments.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param letters
     *            if <code>true</code>, generated string will include alphabetic
     *            characters
     * @param numbers
     *            if <code>true</code>, generated string will include numeric
     *            characters
     * @return the random string
     */
    public static String random(int count, boolean letters, boolean numbers) {
        return random(count, 0, 0, letters, numbers);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of alpha-numeric characters as
     * indicated by the arguments.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param start
     *            the position in set of chars to start at
     * @param end
     *            the position in set of chars to end before
     * @param letters
     *            if <code>true</code>, generated string will include alphabetic
     *            characters
     * @param numbers
     *            if <code>true</code>, generated string will include numeric
     *            characters
     * @return the random string
     */
    public static String random(int count, int start, int end, boolean letters,
            boolean numbers) {
        return random(count, start, end, letters, numbers, null, RANDOM);
    }

    /**
     * <p>
     * Creates a random string based on a variety of options, using default
     * source of randomness.
     * </p>
     * 
     * <p>
     * This method has exactly the same semantics as
     * {@link #random(int,int,int,boolean,boolean,char[],Random)}, but instead
     * of using an externally supplied source of randomness, it uses the
     * internal static {@link Random} instance.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param start
     *            the position in set of chars to start at
     * @param end
     *            the position in set of chars to end before
     * @param letters
     *            only allow letters?
     * @param numbers
     *            only allow numbers?
     * @param chars
     *            the set of chars to choose randoms from. If <code>null</code>,
     *            then it will use the set of all chars.
     * @return the random string
     * @throws ArrayIndexOutOfBoundsException
     *             if there are not <code>(end - start) + 1</code> characters in
     *             the set array.
     */
    public static String random(int count, int start, int end, boolean letters,
            boolean numbers, char[] chars) {
        return random(count, start, end, letters, numbers, chars, RANDOM);
    }

    /**
     * <p>
     * Creates a random string based on a variety of options, using supplied
     * source of randomness.
     * </p>
     * 
     * <p>
     * If start and end are both <code>0</code>, start and end are set to
     * <code>' '</code> and <code>'z'</code>, the ASCII printable characters,
     * will be used, unless letters and numbers are both <code>false</code>, in
     * which case, start and end are set to <code>0</code> and
     * <code>Integer.MAX_VALUE</code>.
     * 
     * <p>
     * If set is not <code>null</code>, characters between start and end are
     * chosen.
     * </p>
     * 
     * <p>
     * This method accepts a user-supplied {@link Random} instance to use as a
     * source of randomness. By seeding a single {@link Random} instance with a
     * fixed seed and using it for each call, the same random sequence of
     * strings can be generated repeatedly and predictably.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param start
     *            the position in set of chars to start at
     * @param end
     *            the position in set of chars to end before
     * @param letters
     *            only allow letters?
     * @param numbers
     *            only allow numbers?
     * @param chars
     *            the set of chars to choose randoms from. If <code>null</code>,
     *            then it will use the set of all chars.
     * @param random
     *            a source of randomness.
     * @return the random string
     * @throws ArrayIndexOutOfBoundsException
     *             if there are not <code>(end - start) + 1</code> characters in
     *             the set array.
     * @throws IllegalArgumentException
     *             if <code>count</code> &lt; 0.
     * @since 2.0
     */
    public static String random(int count, int start, int end, boolean letters,
            boolean numbers, char[] chars, Random random) {
        if (count == 0) {
            return "";
        } else if (count < 0) {
            throw new IllegalArgumentException(
                    "Requested random string length " + count
                            + " is less than 0.");
        }
        if ((start == 0) && (end == 0)) {
            end = 'z' + 1;
            start = ' ';
            if (!letters && !numbers) {
                start = 0;
                end = Integer.MAX_VALUE;
            }
        }

        char[] buffer = new char[count];
        int gap = end - start;

        while (count-- != 0) {
            char ch;
            if (chars == null) {
                ch = (char) (random.nextInt(gap) + start);
            } else {
                ch = chars[random.nextInt(gap) + start];
            }
            if ((letters && Character.isLetter(ch))
                    || (numbers && Character.isDigit(ch))
                    || (!letters && !numbers)) {
                if (ch >= 56320 && ch <= 57343) {
                    if (count == 0) {
                        count++;
                    } else {
                        // low surrogate, insert high surrogate after putting it
                        // in
                        buffer[count] = ch;
                        count--;
                        buffer[count] = (char) (55296 + random.nextInt(128));
                    }
                } else if (ch >= 55296 && ch <= 56191) {
                    if (count == 0) {
                        count++;
                    } else {
                        // high surrogate, insert low surrogate before putting
                        // it in
                        buffer[count] = (char) (56320 + random.nextInt(128));
                        count--;
                        buffer[count] = ch;
                    }
                } else if (ch >= 56192 && ch <= 56319) {
                    // private high surrogate, no effing clue, so skip it
                    count++;
                } else {
                    buffer[count] = ch;
                }
            } else {
                count++;
            }
        }
        return new String(buffer);
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of characters specified.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param chars
     *            the String containing the set of characters to use, may be
     *            null
     * @return the random string
     * @throws IllegalArgumentException
     *             if <code>count</code> &lt; 0.
     */
    public static String random(int count, String chars) {
        if (chars == null) {
            return random(count, 0, 0, false, false, null, RANDOM);
        }
        return random(count, chars.toCharArray());
    }

    /**
     * <p>
     * Creates a random string whose length is the number of characters
     * specified.
     * </p>
     * 
     * <p>
     * Characters will be chosen from the set of characters specified.
     * </p>
     * 
     * @param count
     *            the length of random string to create
     * @param chars
     *            the character array containing the set of characters to use,
     *            may be null
     * @return the random string
     * @throws IllegalArgumentException
     *             if <code>count</code> &lt; 0.
     */
    public static String random(int count, char[] chars) {
        if (chars == null) {
            return random(count, 0, 0, false, false, null, RANDOM);
        }
        return random(count, 0, chars.length, false, false, chars, RANDOM);
    }

    // ///////////////////////////////////////////
    //
    // SerializationUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Serializes an <code>Object</code> to the specified stream.
     * </p>
     * 
     * <p>
     * The stream will be closed once the object is written. This avoids the
     * need for a finally clause, and maybe also exception handling, in the
     * application code.
     * </p>
     * 
     * <p>
     * The stream passed in is not buffered internally within this method. This
     * is the responsibility of your application if desired.
     * </p>
     * 
     * @param obj
     *            the object to serialize to bytes, may be null
     * @param outputStream
     *            the stream to write to, must not be null
     * @throws IllegalArgumentException
     *             if <code>outputStream</code> is <code>null</code>
     * @throws SerializationException
     *             (runtime) if the serialization fails
     */
    public static void serialize(Serializable obj, OutputStream outputStream) {
        if (outputStream == null) {
            throw new IllegalArgumentException(
                    "The OutputStream must not be null");
        }
        ObjectOutputStream out = null;
        try {
            // stream closed in the finally
            out = new ObjectOutputStream(outputStream);
            out.writeObject(obj);

        } catch (IOException ex) {
            throw new SerializationException(ex);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ex) {
                // ignore close exception
            }
        }
    }

    /**
     * <p>
     * Serializes an <code>Object</code> to a byte array for
     * storage/serialization.
     * </p>
     * 
     * @param obj
     *            the object to serialize to bytes
     * @return a byte[] with the converted Serializable
     * @throws SerializationException
     *             (runtime) if the serialization fails
     */
    public static byte[] serialize(Serializable obj) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        serialize(obj, baos);
        return baos.toByteArray();
    }

    // Deserialize
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Deserializes an <code>Object</code> from the specified stream.
     * </p>
     * 
     * <p>
     * The stream will be closed once the object is written. This avoids the
     * need for a finally clause, and maybe also exception handling, in the
     * application code.
     * </p>
     * 
     * <p>
     * The stream passed in is not buffered internally within this method. This
     * is the responsibility of your application if desired.
     * </p>
     * 
     * @param inputStream
     *            the serialized object input stream, must not be null
     * @return the deserialized object
     * @throws IllegalArgumentException
     *             if <code>inputStream</code> is <code>null</code>
     * @throws SerializationException
     *             (runtime) if the serialization fails
     */
    public static Object deserialize(InputStream inputStream) {
        if (inputStream == null) {
            throw new IllegalArgumentException(
                    "The InputStream must not be null");
        }
        ObjectInputStream in = null;
        try {
            // stream closed in the finally
            in = new ObjectInputStream(inputStream);
            return in.readObject();

        } catch (ClassNotFoundException ex) {
            throw new SerializationException(ex);
        } catch (IOException ex) {
            throw new SerializationException(ex);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                // ignore close exception
            }
        }
    }

    /**
     * <p>
     * Deserializes a single <code>Object</code> from an array of bytes.
     * </p>
     * 
     * @param objectData
     *            the serialized object, must not be null
     * @return the deserialized object
     * @throws IllegalArgumentException
     *             if <code>objectData</code> is <code>null</code>
     * @throws SerializationException
     *             (runtime) if the serialization fails
     */
    public static Object deserialize(byte[] objectData) {
        if (objectData == null) {
            throw new IllegalArgumentException("The byte[] must not be null");
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
        return deserialize(bais);
    }

    // ///////////////////////////////////////////
    //
    // Validate
    //
    // ///////////////////////////////////////////

    private static final String DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE = "The value %s is not in the specified exclusive range of %s to %s";
    private static final String DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE = "The value %s is not in the specified inclusive range of %s to %s";
    private static final String DEFAULT_MATCHES_PATTERN_EX = "The string %s does not match the pattern %s";
    private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null";
    private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false";
    private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE = "The validated array contains null element at index: %d";
    private static final String DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE = "The validated collection contains null element at index: %d";
    private static final String DEFAULT_NOT_BLANK_EX_MESSAGE = "The validated character sequence is blank";
    private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty";
    private static final String DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE = "The validated character sequence is empty";
    private static final String DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE = "The validated collection is empty";
    private static final String DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE = "The validated map is empty";
    private static final String DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE = "The validated array index is invalid: %d";
    private static final String DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE = "The validated character sequence index is invalid: %d";
    private static final String DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE = "The validated collection index is invalid: %d";
    private static final String DEFAULT_VALID_STATE_EX_MESSAGE = "The validated state is false";
    private static final String DEFAULT_IS_ASSIGNABLE_EX_MESSAGE = "The validated class can not be converted to the %s class";
    private static final String DEFAULT_IS_INSTANCE_OF_EX_MESSAGE = "The validated object is not an instance of %s";

    /**
     * <p>
     * Validate that the argument condition is <code>true</code>; otherwise
     * throwing an exception with the specified message. This method is useful
     * when validating according to an arbitrary boolean expression, such as
     * validating a primitive number or using your own custom validation
     * expression.
     * </p>
     * 
     * <pre>
     * Validate.isTrue(i &gt; 0.0, &quot;The value must be greater than zero: %d&quot;, i);
     * </pre>
     * 
     * <p>
     * For performance reasons, the long value is passed as a separate parameter
     * and appended to the exception message only in the case of an error.
     * </p>
     * 
     * @param expression
     *            the boolean expression to check
     * @param message
     *            the exception message if invalid
     * @param value
     *            the value to append to the message when invalid
     * @throws IllegalArgumentException
     *             if expression is <code>false</code>
     * @see #isTrue(boolean)
     * @see #isTrue(boolean, String, double)
     * @see #isTrue(boolean, String, Object...)
     */
    public static void asserts(boolean expression, String message, long value) {
        if (expression == false) {
            throw new IllegalArgumentException(String.format(message,
                    Long.valueOf(value)));
        }
    }

    /**
     * <p>
     * Validate that the argument condition is <code>true</code>; otherwise
     * throwing an exception with the specified message. This method is useful
     * when validating according to an arbitrary boolean expression, such as
     * validating a primitive number or using your own custom validation
     * expression.
     * </p>
     * 
     * <pre>
     * Validate.isTrue(d &gt; 0.0, &quot;The value must be greater than zero: %s&quot;, d);
     * </pre>
     * 
     * <p>
     * For performance reasons, the double value is passed as a separate
     * parameter and appended to the exception message only in the case of an
     * error.
     * </p>
     * 
     * @param expression
     *            the boolean expression to check
     * @param message
     *            the exception message if invalid
     * @param value
     *            the value to append to the message when invalid
     * @throws IllegalArgumentException
     *             if expression is <code>false</code>
     * @see #isTrue(boolean)
     * @see #isTrue(boolean, String, long)
     * @see #isTrue(boolean, String, Object...)
     */
    public static void asserts(boolean expression, String message, double value) {
        if (expression == false) {
            throw new IllegalArgumentException(String.format(message,
                    new Double(value)));
        }
    }

    /**
     * <p>
     * Validate that the argument condition is <code>true</code>; otherwise
     * throwing an exception with the specified message. This method is useful
     * when validating according to an arbitrary boolean expression, such as
     * validating a primitive number or using your own custom validation
     * expression.
     * </p>
     * 
     * <pre>
     * Validate.isTrue(i &gt;= min &amp;&amp; i &lt;= max, &quot;The value must be between %d and %d&quot;,
     *         min, max);
     * Validate.isTrue(myObject.isOk(), &quot;The object is not okay&quot;);
     * </pre>
     * 
     * @param expression
     *            the boolean expression to check
     * @param message
     *            the exception message if invalid
     * @param values
     *            the optional values for the formatted exception message
     * @throws IllegalArgumentException
     *             if expression is <code>false</code>
     * @see #isTrue(boolean)
     * @see #isTrue(boolean, String, long)
     * @see #isTrue(boolean, String, double)
     */
    public static void asserts(boolean expression, String message,
            Object... values) {
        if (expression == false) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the argument condition is <code>true</code>; otherwise
     * throwing an exception. This method is useful when validating according to
     * an arbitrary boolean expression, such as validating a primitive number or
     * using your own custom validation expression.
     * </p>
     * 
     * <pre>
     * Validate.isTrue(i &gt; 0);
     * Validate.isTrue(myObject.isOk());
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated expression is
     * false&quot;.
     * </p>
     * 
     * @param expression
     *            the boolean expression to check
     * @throws IllegalArgumentException
     *             if expression is <code>false</code>
     * @see #isTrue(boolean, String, long)
     * @see #isTrue(boolean, String, double)
     * @see #isTrue(boolean, String, Object...)
     */
    public static void asserts(boolean expression) {
        if (expression == false) {
            throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE);
        }
    }

    // notNull
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument is not <code>null</code>; otherwise
     * throwing an exception.
     * 
     * <pre>
     * Validate.notNull(myObject, &quot;The object must not be null&quot;);
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated object is null&quot;.
     * </p>
     * 
     * @param <T>
     *            the object type
     * @param object
     *            the object to check
     * @return the validated object (never <code>null</code> for method
     *         chaining)
     * @throws NullPointerException
     *             if the object is <code>null</code>
     * @see #notNull(Object, String, Object...)
     */
    public static <T> T notNull(T object) {
        return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE);
    }

    /**
     * <p>
     * Validate that the specified argument is not <code>null</code>; otherwise
     * throwing an exception with the specified message.
     * 
     * <pre>
     * Validate.notNull(myObject, &quot;The object must not be null&quot;);
     * </pre>
     * 
     * @param <T>
     *            the object type
     * @param object
     *            the object to check
     * @param message
     *            the exception message if invalid
     * @param values
     *            the optional values for the formatted exception message
     * @return the validated object (never <code>null</code> for method
     *         chaining)
     * @throws NullPointerException
     *             if the object is <code>null</code>
     * @see #notNull(Object)
     */
    public static <T> T notNull(T object, String message, Object... values) {
        if (object == null) {
            throw new NullPointerException(String.format(message, values));
        }
        return object;
    }

    // notEmpty array
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument array is neither <code>null</code>
     * nor a length of zero (no elements); otherwise throwing an exception with
     * the specified message.
     * 
     * <pre>
     * Validate.notEmpty(myArray, &quot;The array must not be empty&quot;);
     * </pre>
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @param message
     *            the exception message if invalid
     * @return the validated array (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if the array is empty
     * @see #notEmpty(Object[])
     */
    public static <T> T[] notEmpty(T[] array, String message, Object... values) {
        if (array == null) {
            throw new NullPointerException(String.format(message, values));
        }
        if (array.length == 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
        return array;
    }

    /**
     * <p>
     * Validate that the specified argument array is neither <code>null</code>
     * nor a length of zero (no elements); otherwise throwing an exception.
     * 
     * <pre>
     * Validate.notEmpty(myArray);
     * </pre>
     * 
     * <p>
     * The message in the exception is &quot;The validated array is empty&quot;.
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @return the validated array (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if the array is empty
     * @see #notEmpty(Object[], String, Object...)
     */
    public static <T> T[] notEmpty(T[] array) {
        return notEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE);
    }

    // notEmpty collection
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument collection is neither
     * <code>null</code> nor a size of zero (no elements); otherwise throwing an
     * exception with the specified message.
     * 
     * <pre>
     * Validate.notEmpty(myCollection, &quot;The collection must not be empty&quot;);
     * </pre>
     * 
     * @param <T>
     *            the collection type
     * @param collection
     *            the collection to check
     * @param message
     *            the exception message if invalid
     * @return the validated collection (never <code>null</code> method for
     *         chaining)
     * @throws NullPointerException
     *             if the collection is <code>null</code>
     * @throws IllegalArgumentException
     *             if the collection is empty
     * @see #notEmpty(Object[])
     */
    public static <T extends Collection<?>> T notEmpty(T collection,
            String message, Object... values) {
        if (collection == null) {
            throw new NullPointerException(String.format(message, values));
        }
        if (collection.size() == 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
        return collection;
    }

    /**
     * <p>
     * Validate that the specified argument collection is neither
     * <code>null</code> nor a size of zero (no elements); otherwise throwing an
     * exception.
     * 
     * <pre>
     * Validate.notEmpty(myCollection);
     * </pre>
     * 
     * <p>
     * The message in the exception is &quot;The validated collection is
     * empty&quot;.
     * </p>
     * 
     * @param <T>
     *            the collection type
     * @param collection
     *            the collection to check
     * @return the validated collection (never <code>null</code> method for
     *         chaining)
     * @throws NullPointerException
     *             if the collection is <code>null</code>
     * @throws IllegalArgumentException
     *             if the collection is empty
     * @see #notEmpty(Collection, String, Object...)
     */
    public static <T extends Collection<?>> T notEmpty(T collection) {
        return notEmpty(collection, DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE);
    }

    // notEmpty map
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument map is neither <code>null</code> nor
     * a size of zero (no elements); otherwise throwing an exception with the
     * specified message.
     * 
     * <pre>
     * Validate.notEmpty(myMap, &quot;The map must not be empty&quot;);
     * </pre>
     * 
     * @param <T>
     *            the map type
     * @param map
     *            the map to check
     * @param message
     *            the exception message if invalid
     * @return the validated map (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the map is <code>null</code>
     * @throws IllegalArgumentException
     *             if the map is empty
     * @see #notEmpty(Object[])
     */
    public static <T extends Map<?, ?>> T notEmpty(T map, String message,
            Object... values) {
        if (map == null) {
            throw new NullPointerException(String.format(message, values));
        }
        if (map.size() == 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
        return map;
    }

    /**
     * <p>
     * Validate that the specified argument map is neither <code>null</code> nor
     * a size of zero (no elements); otherwise throwing an exception.
     * 
     * <pre>
     * Validate.notEmpty(myMap);
     * </pre>
     * 
     * <p>
     * The message in the exception is &quot;The validated map is empty&quot;.
     * </p>
     * 
     * @param <T>
     *            the map type
     * @param map
     *            the map to check
     * @return the validated map (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the map is <code>null</code>
     * @throws IllegalArgumentException
     *             if the map is empty
     * @see #notEmpty(Map, String, Object...)
     */
    public static <T extends Map<?, ?>> T notEmpty(T map) {
        return notEmpty(map, DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE);
    }

    // notEmpty string
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument character sequence is neither
     * <code>null</code> nor a length of zero (no characters); otherwise
     * throwing an exception with the specified message.
     * 
     * <pre>
     * Validate.notEmpty(myString, &quot;The string must not be empty&quot;);
     * </pre>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @param message
     *            the exception message if invalid
     * @return the validated character sequence (never <code>null</code> method
     *         for chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IllegalArgumentException
     *             if the character sequence is empty
     * @see #notEmpty(CharSequence)
     */
    public static <T extends CharSequence> T notEmpty(T chars, String message,
            Object... values) {
        if (chars == null) {
            throw new NullPointerException(String.format(message, values));
        }
        if (chars.length() == 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
        return chars;
    }

    /**
     * <p>
     * Validate that the specified argument character sequence is neither
     * <code>null</code> nor a length of zero (no characters); otherwise
     * throwing an exception with the specified message.
     * 
     * <pre>
     * Validate.notEmpty(myString);
     * </pre>
     * 
     * <p>
     * The message in the exception is &quot;The validated character sequence is
     * empty&quot;.
     * </p>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @return the validated character sequence (never <code>null</code> method
     *         for chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IllegalArgumentException
     *             if the character sequence is empty
     * @see #notEmpty(CharSequence, String, Object...)
     */
    public static <T extends CharSequence> T notEmpty(T chars) {
        return notEmpty(chars, DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE);
    }

    // notBlank string
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument character sequence is neither
     * <code>null</code>, a length of zero (no characters), empty nor
     * whitespace; otherwise throwing an exception with the specified message.
     * 
     * <pre>
     * Validate.notBlank(myString, &quot;The string must not be blank&quot;);
     * </pre>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @param message
     *            the exception message if invalid
     * @return the validated character sequence (never <code>null</code> method
     *         for chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IllegalArgumentException
     *             if the character sequence is blank
     * @see #notBlank(CharSequence)
     */
    public static <T extends CharSequence> T notBlank(T chars, String message,
            Object... values) {
        if (chars == null) {
            throw new NullPointerException(String.format(message, values));
        }
        if (isBlank(chars)) {
            throw new IllegalArgumentException(String.format(message, values));
        }
        return chars;
    }

    /**
     * <p>
     * Validate that the specified argument character sequence is neither
     * <code>null</code>, a length of zero (no characters), empty nor
     * whitespace; otherwise throwing an exception.
     * 
     * <pre>
     * Validate.notBlank(myString);
     * </pre>
     * 
     * <p>
     * The message in the exception is &quot;The validated character sequence is
     * blank&quot;.
     * </p>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @return the validated character sequence (never <code>null</code> method
     *         for chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IllegalArgumentException
     *             if the character sequence is blank
     * @see #notBlank(CharSequence, String, Object...)
     */
    public static <T extends CharSequence> T notBlank(T chars) {
        return notBlank(chars, DEFAULT_NOT_BLANK_EX_MESSAGE);
    }

    // noNullElements array
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument array is neither <code>null</code>
     * nor contains any elements that are <code>null</code>; otherwise throwing
     * an exception with the specified message.
     * 
     * <pre>
     * Validate.noNullElements(myArray, &quot;The array contain null at position %d&quot;);
     * </pre>
     * 
     * <p>
     * If the array is <code>null</code>, then the message in the exception is
     * &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the array has a <code>null</code> element, then the iteration index of
     * the invalid element is appended to the <code>values</code> argument.
     * </p>
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @return the validated array (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if an element is <code>null</code>
     * @see #noNullElements(Object[])
     */
    public static <T> T[] noNullElements(T[] array, String message,
            Object... values) {
        notNull(array);
        for (int i = 0; i < array.length; i++) {
            if (array[i] == null) {
                Object[] values2 = add(values, Integer.valueOf(i));
                throw new IllegalArgumentException(String.format(message,
                        values2));
            }
        }
        return array;
    }

    /**
     * <p>
     * Validate that the specified argument array is neither <code>null</code>
     * nor contains any elements that are <code>null</code>; otherwise throwing
     * an exception.
     * 
     * <pre>
     * Validate.noNullElements(myArray);
     * </pre>
     * 
     * <p>
     * If the array is <code>null</code>, then the message in the exception is
     * &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the array has a <code>null</code> element, then the message in the
     * exception is &quot;The validated array contains null element at index:
     * &quot followed by the index.
     * </p>
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @return the validated array (never <code>null</code> method for chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if an element is <code>null</code>
     * @see #noNullElements(Object[], String, Object...)
     */
    public static <T> T[] noNullElements(T[] array) {
        return noNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE);
    }

    // noNullElements iterable
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validate that the specified argument iterable is neither
     * <code>null</code> nor contains any elements that are <code>null</code>;
     * otherwise throwing an exception with the specified message.
     * 
     * <pre>
     * Validate.noNullElements(myCollection,
     *         &quot;The collection contains null at position %d&quot;);
     * </pre>
     * 
     * <p>
     * If the iterable is <code>null</code>, then the message in the exception
     * is &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the iterable has a <code>null</code> element, then the iteration index
     * of the invalid element is appended to the <code>values</code> argument.
     * </p>
     * 
     * @param <T>
     *            the iterable type
     * @param iterable
     *            the iterable to check
     * @return the validated iterable (never <code>null</code> method for
     *         chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if an element is <code>null</code>
     * @see #noNullElements(Iterable)
     */
    public static <T extends Iterable<?>> T noNullElements(T iterable,
            String message, Object... values) {
        notNull(iterable);
        int i = 0;
        for (Iterator<?> it = iterable.iterator(); it.hasNext(); i++) {
            if (it.next() == null) {
                Object[] values2 = addAll(values, Integer.valueOf(i));
                throw new IllegalArgumentException(String.format(message,
                        values2));
            }
        }
        return iterable;
    }

    /**
     * <p>
     * Validate that the specified argument iterable is neither
     * <code>null</code> nor contains any elements that are <code>null</code>;
     * otherwise throwing an exception.
     * 
     * <pre>
     * Validate.noNullElements(myCollection);
     * </pre>
     * 
     * <p>
     * If the iterable is <code>null</code>, then the message in the exception
     * is &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the array has a <code>null</code> element, then the message in the
     * exception is &quot;The validated iterable contains null element at index:
     * &quot followed by the index.
     * </p>
     * 
     * @param <T>
     *            the iterable type
     * @param iterable
     *            the iterable to check
     * @return the validated iterable (never <code>null</code> method for
     *         chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IllegalArgumentException
     *             if an element is <code>null</code>
     * @see #noNullElements(Iterable, String, Object...)
     */
    public static <T extends Iterable<?>> T noNullElements(T iterable) {
        return noNullElements(iterable,
                DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE);
    }

    // validIndex array
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validates that the index is within the bounds of the argument array;
     * otherwise throwing an exception with the specified message.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myArray, 2, &quot;The array index is invalid: &quot;);
     * </pre>
     * 
     * <p>
     * If the array is <code>null</code>, then the message of the exception is
     * &quot;The validated object is null&quot;.
     * </p>
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @param index
     *            the index
     * @param message
     *            the exception message if invalid
     * @return the validated array (never <code>null</code> for method chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(Object[], int)
     */
    public static <T> T[] validIndex(T[] array, int index, String message,
            Object... values) {
        notNull(array);
        if (index < 0 || index >= array.length) {
            throw new IndexOutOfBoundsException(String.format(message, values));
        }
        return array;
    }

    /**
     * <p>
     * Validates that the index is within the bounds of the argument array;
     * otherwise throwing an exception.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myArray, 2);
     * </pre>
     * 
     * <p>
     * If the array is <code>null</code>, then the message of the exception is
     * &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the index is invalid, then the message of the exception is &quot;The
     * validated array index is invalid: &quot; followed by the index.
     * </p>
     * 
     * @param <T>
     *            the array type
     * @param array
     *            the array to check
     * @param index
     *            the index
     * @return the validated array (never <code>null</code> for method chaining)
     * @throws NullPointerException
     *             if the array is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(Object[], int, String, Object...)
     */
    public static <T> T[] validIndex(T[] array, int index) {
        return validIndex(array, index, DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE,
                Integer.valueOf(index));
    }

    // validIndex collection
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validates that the index is within the bounds of the argument collection;
     * otherwise throwing an exception with the specified message.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myCollection, 2, &quot;The collection index is invalid: &quot;);
     * </pre>
     * 
     * <p>
     * If the collection is <code>null</code>, then the message of the exception
     * is &quot;The validated object is null&quot;.
     * </p>
     * 
     * @param <T>
     *            the collection type
     * @param collection
     *            the collection to check
     * @param index
     *            the index
     * @param message
     *            the exception message if invalid
     * @return the validated collection (never <code>null</code> for chaining)
     * @throws NullPointerException
     *             if the collection is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(Collection, int)
     */
    public static <T extends Collection<?>> T validIndex(T collection,
            int index, String message, Object... values) {
        notNull(collection);
        if (index < 0 || index >= collection.size()) {
            throw new IndexOutOfBoundsException(String.format(message, values));
        }
        return collection;
    }

    /**
     * <p>
     * Validates that the index is within the bounds of the argument collection;
     * otherwise throwing an exception.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myCollection, 2);
     * </pre>
     * 
     * <p>
     * If the index is invalid, then the message of the exception is &quot;The
     * validated collection index is invalid: &quot; followed by the index.
     * </p>
     * 
     * @param <T>
     *            the collection type
     * @param collection
     *            the collection to check
     * @param index
     *            the index
     * @return the validated collection (never <code>null</code> for method
     *         chaining)
     * @throws NullPointerException
     *             if the collection is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(Collection, int, String, Object...)
     */
    public static <T extends Collection<?>> T validIndex(T collection, int index) {
        return validIndex(collection, index,
                DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE,
                Integer.valueOf(index));
    }

    // validIndex string
    // ---------------------------------------------------------------------------------

    /**
     * <p>
     * Validates that the index is within the bounds of the argument character
     * sequence; otherwise throwing an exception with the specified message.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myStr, 2, &quot;The string index is invalid: &quot;);
     * </pre>
     * 
     * <p>
     * If the character sequence is <code>null</code>, then the message of the
     * exception is &quot;The validated object is null&quot;.
     * </p>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @param index
     *            the index
     * @param message
     *            the exception message if invalid
     * @return the validated character sequence (never <code>null</code> for
     *         method chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(CharSequence, int)
     */
    public static <T extends CharSequence> T validIndex(T chars, int index,
            String message, Object... values) {
        notNull(chars);
        if (index < 0 || index >= chars.length()) {
            throw new IndexOutOfBoundsException(String.format(message, values));
        }
        return chars;
    }

    /**
     * <p>
     * Validates that the index is within the bounds of the argument character
     * sequence; otherwise throwing an exception.
     * </p>
     * 
     * <pre>
     * Validate.validIndex(myStr, 2);
     * </pre>
     * 
     * <p>
     * If the character sequence is <code>null</code>, then the message of the
     * exception is &quot;The validated object is null&quot;.
     * </p>
     * 
     * <p>
     * If the index is invalid, then the message of the exception is &quot;The
     * validated character sequence index is invalid: &quot; followed by the
     * index.
     * </p>
     * 
     * @param <T>
     *            the character sequence type
     * @param chars
     *            the character sequence to check
     * @param index
     *            the index
     * @return the validated character sequence (never <code>null</code> for
     *         method chaining)
     * @throws NullPointerException
     *             if the character sequence is <code>null</code>
     * @throws IndexOutOfBoundsException
     *             if the index is invalid
     * @see #validIndex(CharSequence, int, String, Object...)
     */
    public static <T extends CharSequence> T validIndex(T chars, int index) {
        return validIndex(chars, index,
                DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE,
                Integer.valueOf(index));
    }

    /**
     * <p>
     * Validate that the stateful condition is <code>true</code>; otherwise
     * throwing an exception. This method is useful when validating according to
     * an arbitrary boolean expression, such as validating a primitive number or
     * using your own custom validation expression.
     * </p>
     * 
     * <pre>
     * Validate.validState(field &gt; 0);
     * Validate.validState(this.isOk());
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated state is false&quot;.
     * </p>
     * 
     * @param expression
     *            the boolean expression to check
     * @throws IllegalStateException
     *             if expression is <code>false</code>
     * @see #validState(boolean, String, Object...)
     */
    public static void validState(boolean expression) {
        if (expression == false) {
            throw new IllegalArgumentException(DEFAULT_VALID_STATE_EX_MESSAGE);
        }
    }

    /**
     * <p>
     * Validate that the stateful condition is <code>true</code>; otherwise
     * throwing an exception with the specified message. This method is useful
     * when validating according to an arbitrary boolean expression, such as
     * validating a primitive number or using your own custom validation
     * expression.
     * </p>
     * 
     * <pre>
     * Validate.validState(this.isOk(), &quot;The state is not OK: %s&quot;, myObject);
     * </pre>
     * 
     * @param expression
     *            the boolean expression to check
     * @param message
     *            the exception message if invalid
     * @param values
     *            the optional values for the formatted exception message
     * @throws IllegalStateException
     *             if expression is <code>false</code>
     * @see #validState(boolean)
     */
    public static void validState(boolean expression, String message,
            Object... values) {
        if (expression == false) {
            throw new IllegalStateException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the specified argument character sequence matches the
     * specified regular expression pattern; otherwise throwing an exception.
     * </p>
     * 
     * <pre>
     * Validate.matchesPattern(&quot;hi&quot;, &quot;[a-z]*&quot;);
     * </pre>
     * 
     * <p>
     * The syntax of the pattern is the one used in the {@link Pattern} class.
     * </p>
     * 
     * @param input
     *            the character sequence to validate
     * @param pattern
     *            regular expression pattern
     * @throws IllegalArgumentException
     *             if the character sequence does not match the pattern
     * @see #matchesPattern(CharSequence, String, String, Object...)
     */
    public static void matchesPattern(CharSequence input, String pattern) {
        if (Pattern.matches(pattern, input) == false) {
            throw new IllegalArgumentException(String.format(
                    DEFAULT_MATCHES_PATTERN_EX, input, pattern));
        }
    }

    /**
     * <p>
     * Validate that the specified argument character sequence matches the
     * specified regular expression pattern; otherwise throwing an exception
     * with the specified message.
     * </p>
     * 
     * <pre>
     * Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");
     * </pre>
     * 
     * <p>
     * The syntax of the pattern is the one used in the {@link Pattern} class.
     * </p>
     * 
     * @param input
     *            the character sequence to validate
     * @param pattern
     *            regular expression pattern
     * @param message
     *            the exception message
     * @param values
     *            (optional) values to replace in the exception message
     * @throws IllegalArgumentException
     *             if the character sequence does not match the pattern
     * @see #matchesPattern(CharSequence, String)
     */
    public static void matchesPattern(CharSequence input, String pattern,
            String message, Object... values) {
        if (Pattern.matches(pattern, input) == false) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the specified argument object fall between the two
     * inclusive values specified; otherwise, throws an exception.
     * </p>
     * 
     * <pre>
     * Validate.inclusiveBetween(0, 2, 1);
     * </pre>
     * 
     * @param value
     *            the object to validate
     * @param start
     *            the inclusive start value
     * @param end
     *            the inclusive end value
     * @throws IllegalArgumentException
     *             if the value falls out of the boundaries
     * @see #inclusiveBetween(Object, Object, Comparable, String, Object...)
     */
    public static <T> void inclusiveBetween(T start, T end, Comparable<T> value) {
        if (value.compareTo(start) < 0 || value.compareTo(end) > 0) {
            throw new IllegalArgumentException(String.format(
                    DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end));
        }
    }

    /**
     * <p>
     * Validate that the specified argument object fall between the two
     * inclusive values specified; otherwise, throws an exception with the
     * specified message.
     * </p>
     * 
     * <pre>
     * Validate.inclusiveBetween(0, 2, 1, &quot;Not in boundaries&quot;);
     * </pre>
     * 
     * @param value
     *            the object to validate
     * @param start
     *            the inclusive start value
     * @param end
     *            the inclusive end value
     * @param message
     *            the exception message
     * @param values
     *            to replace in the exception message (optional)
     * @throws IllegalArgumentException
     *             if the value falls out of the boundaries
     * @see #inclusiveBetween(Object, Object, Comparable)
     */
    public static <T> void inclusiveBetween(T start, T end,
            Comparable<T> value, String message, Object... values) {
        if (value.compareTo(start) < 0 || value.compareTo(end) > 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the specified argument object fall between the two
     * exclusive values specified; otherwise, throws an exception.
     * </p>
     * 
     * <pre>
     * Validate.inclusiveBetween(0, 2, 1);
     * </pre>
     * 
     * @param value
     *            the object to validate
     * @param start
     *            the exclusive start value
     * @param end
     *            the exclusive end value
     * @throws IllegalArgumentException
     *             if the value falls out of the boundaries
     * @see #exclusiveBetween(Object, Object, Comparable, String, Object...)
     */
    public static <T> void exclusiveBetween(T start, T end, Comparable<T> value) {
        if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) {
            throw new IllegalArgumentException(String.format(
                    DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end));
        }
    }

    /**
     * <p>
     * Validate that the specified argument object fall between the two
     * exclusive values specified; otherwise, throws an exception with the
     * specified message.
     * </p>
     * 
     * <pre>
     * Validate.inclusiveBetween(0, 2, 1, &quot;Not in boundaries&quot;);
     * </pre>
     * 
     * @param value
     *            the object to validate
     * @param start
     *            the exclusive start value
     * @param end
     *            the exclusive end value
     * @param message
     *            the exception message
     * @param values
     *            to replace in the exception message (optional)
     * @throws IllegalArgumentException
     *             if the value falls out of the boundaries
     * @see #exclusiveBetween(Object, Object, Comparable)
     */
    public static <T> void exclusiveBetween(T start, T end,
            Comparable<T> value, String message, Object... values) {
        if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the argument is an instance of the specified class;
     * otherwise throwing an exception. This method is useful when validating
     * according to an arbitrary class
     * </p>
     * 
     * <pre>
     * Validate.isInstanceOf(OkClass.class, object);
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated object is not an
     * instance of&quot; followed by the name of the class
     * </p>
     * 
     * @param type
     *            the class the object must be validated against
     * @param o
     *            the object to check
     * @throws IllegalArgumentException
     *             if argument is not of specified class
     * @see #isInstanceOf(Class, Object, String, Object...)
     */
    public static void isInstanceOf(Class<?> type, Object o) {
        if (type.isInstance(o) == false) {
            throw new IllegalArgumentException(String.format(
                    DEFAULT_IS_INSTANCE_OF_EX_MESSAGE, type.getName()));
        }
    }

    /**
     * <p>
     * Validate that the argument is an instance of the specified class;
     * otherwise throwing an exception with the specified message. This method
     * is useful when validating according to an arbitrary class
     * </p>
     * 
     * <pre>
     * Validate.isInstanceOf(OkClass.classs, object,
     *         &quot;Wrong class, object is of class %s&quot;, object.getClass().getName());
     * </pre>
     * 
     * @param type
     *            the class the object must be validated against
     * @param o
     *            the object to check
     * @param message
     *            exception message
     * @param values
     *            optional value for the exception message
     * @throws IllegalArgumentException
     *             if argument is not of specified class
     * @see #isInstanceOf(Class, Object)
     */
    public static void isInstanceOf(Class<?> type, Object o, String message,
            Object... values) {
        if (type.isInstance(o) == false) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    /**
     * <p>
     * Validate that the argument can be converted to the specified class;
     * otherwise throwing an exception with the specified message. This method
     * is useful when validating if there will be no casting errors.
     * </p>
     * 
     * <pre>
     * Validate.isAssignableFrom(SuperClass.class, object.getClass());
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated object can not be
     * converted to the&quot; followed by the name of the class and
     * &quot;class&quot;
     * </p>
     * 
     * @param superType
     *            the class the class must be validated against
     * @param type
     *            the class to check
     * @throws IllegalArgumentException
     *             if argument can not be converted to the specified class
     * @see #isAssignableFrom(Class, Class, String, Object...)
     */
    public static void isAssignableFrom(Class<?> superType, Class<?> type) {
        if (superType.isAssignableFrom(type) == false) {
            throw new IllegalArgumentException(String.format(
                    DEFAULT_IS_ASSIGNABLE_EX_MESSAGE, superType.getName()));
        }
    }

    /**
     * <p>
     * Validate that the argument can be converted to the specified class;
     * otherwise throwing an exception. This method is useful when validating if
     * there will be no casting errors.
     * </p>
     * 
     * <pre>
     * Validate.isAssignableFrom(SuperClass.class, object.getClass());
     * </pre>
     * 
     * <p>
     * The message of the exception is &quot;The validated object can not be
     * converted to the&quot; followed by the name of the class and
     * &quot;class&quot;
     * </p>
     * 
     * @param superType
     *            the class the class must be validated against
     * @param type
     *            the class to check
     * @param message
     *            the exception message if invalid
     * @param values
     *            the optional values for the formatted exception message
     * @throws IllegalArgumentException
     *             if argument can not be converted to the specified class
     * @see #isAssignableFrom(Class, Class)
     */
    public static void isAssignableFrom(Class<?> superType, Class<?> type,
            String message, Object... values) {
        if (superType.isAssignableFrom(type) == false) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }

    // ///////////////////////////////////////////
    //
    // NumberUtils
    //
    // ///////////////////////////////////////////

    /** Reusable Long constant for zero. */
    public static final Long LONG_ZERO = new Long(0L);
    /** Reusable Long constant for one. */
    public static final Long LONG_ONE = new Long(1L);
    /** Reusable Long constant for minus one. */
    public static final Long LONG_MINUS_ONE = new Long(-1L);
    /** Reusable Integer constant for zero. */
    public static final Integer INTEGER_ZERO = new Integer(0);
    /** Reusable Integer constant for one. */
    public static final Integer INTEGER_ONE = new Integer(1);
    /** Reusable Integer constant for minus one. */
    public static final Integer INTEGER_MINUS_ONE = new Integer(-1);
    /** Reusable Short constant for zero. */
    public static final Short SHORT_ZERO = new Short((short) 0);
    /** Reusable Short constant for one. */
    public static final Short SHORT_ONE = new Short((short) 1);
    /** Reusable Short constant for minus one. */
    public static final Short SHORT_MINUS_ONE = new Short((short) -1);
    /** Reusable Byte constant for zero. */
    public static final Byte BYTE_ZERO = Byte.valueOf((byte) 0);
    /** Reusable Byte constant for one. */
    public static final Byte BYTE_ONE = Byte.valueOf((byte) 1);
    /** Reusable Byte constant for minus one. */
    public static final Byte BYTE_MINUS_ONE = Byte.valueOf((byte) -1);
    /** Reusable Double constant for zero. */
    public static final Double DOUBLE_ZERO = new Double(0.0d);
    /** Reusable Double constant for one. */
    public static final Double DOUBLE_ONE = new Double(1.0d);
    /** Reusable Double constant for minus one. */
    public static final Double DOUBLE_MINUS_ONE = new Double(-1.0d);
    /** Reusable Float constant for zero. */
    public static final Float FLOAT_ZERO = new Float(0.0f);
    /** Reusable Float constant for one. */
    public static final Float FLOAT_ONE = new Float(1.0f);
    /** Reusable Float constant for minus one. */
    public static final Float FLOAT_MINUS_ONE = new Float(-1.0f);

    /**
     * <p>
     * Convert a <code>String</code> to an <code>int</code>, returning
     * <code>zero</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, <code>zero</code> is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toInt(null) = 0
     *   NumberUtils.toInt("")   = 0
     *   NumberUtils.toInt("1")  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @return the int represented by the string, or <code>zero</code> if
     *         conversion fails
     * @since 2.1
     */
    public static int toInt(String str) {
        return toInt(str, 0);
    }

    /**
     * <p>
     * Convert a <code>String</code> to an <code>int</code>, returning a default
     * value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, the default value is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toInt(null, 1) = 1
     *   NumberUtils.toInt("", 1)   = 1
     *   NumberUtils.toInt("1", 0)  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @param defaultValue
     *            the default value
     * @return the int represented by the string, or the default if conversion
     *         fails
     * @since 2.1
     */
    public static int toInt(String str, int defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>long</code>, returning
     * <code>zero</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, <code>zero</code> is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toLong(null) = 0L
     *   NumberUtils.toLong("")   = 0L
     *   NumberUtils.toLong("1")  = 1L
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @return the long represented by the string, or <code>0</code> if
     *         conversion fails
     * @since 2.1
     */
    public static long toLong(String str) {
        return toLong(str, 0L);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>long</code>, returning a default
     * value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, the default value is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toLong(null, 1L) = 1L
     *   NumberUtils.toLong("", 1L)   = 1L
     *   NumberUtils.toLong("1", 0L)  = 1L
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @param defaultValue
     *            the default value
     * @return the long represented by the string, or the default if conversion
     *         fails
     * @since 2.1
     */
    public static long toLong(String str, long defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Long.parseLong(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>float</code>, returning
     * <code>0.0f</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string <code>str</code> is <code>null</code>, <code>0.0f</code> is
     * returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toFloat(null)   = 0.0f
     *   NumberUtils.toFloat("")     = 0.0f
     *   NumberUtils.toFloat("1.5")  = 1.5f
     * </pre>
     * 
     * @param str
     *            the string to convert, may be <code>null</code>
     * @return the float represented by the string, or <code>0.0f</code> if
     *         conversion fails
     * @since 2.1
     */
    public static float toFloat(String str) {
        return toFloat(str, 0.0f);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>float</code>, returning a
     * default value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string <code>str</code> is <code>null</code>, the default value is
     * returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toFloat(null, 1.1f)   = 1.0f
     *   NumberUtils.toFloat("", 1.1f)     = 1.1f
     *   NumberUtils.toFloat("1.5", 0.0f)  = 1.5f
     * </pre>
     * 
     * @param str
     *            the string to convert, may be <code>null</code>
     * @param defaultValue
     *            the default value
     * @return the float represented by the string, or defaultValue if
     *         conversion fails
     * @since 2.1
     */
    public static float toFloat(String str, float defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Float.parseFloat(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>double</code>, returning
     * <code>0.0d</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string <code>str</code> is <code>null</code>, <code>0.0d</code> is
     * returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toDouble(null)   = 0.0d
     *   NumberUtils.toDouble("")     = 0.0d
     *   NumberUtils.toDouble("1.5")  = 1.5d
     * </pre>
     * 
     * @param str
     *            the string to convert, may be <code>null</code>
     * @return the double represented by the string, or <code>0.0d</code> if
     *         conversion fails
     * @since 2.1
     */
    public static double toDouble(String str) {
        return toDouble(str, 0.0d);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>double</code>, returning a
     * default value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string <code>str</code> is <code>null</code>, the default value is
     * returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toDouble(null, 1.1d)   = 1.1d
     *   NumberUtils.toDouble("", 1.1d)     = 1.1d
     *   NumberUtils.toDouble("1.5", 0.0d)  = 1.5d
     * </pre>
     * 
     * @param str
     *            the string to convert, may be <code>null</code>
     * @param defaultValue
     *            the default value
     * @return the double represented by the string, or defaultValue if
     *         conversion fails
     * @since 2.1
     */
    public static double toDouble(String str, double defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Double.parseDouble(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Convert a <code>String</code> to a <code>byte</code>, returning
     * <code>zero</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, <code>zero</code> is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toByte(null) = 0
     *   NumberUtils.toByte("")   = 0
     *   NumberUtils.toByte("1")  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @return the byte represented by the string, or <code>zero</code> if
     *         conversion fails
     * @since 2.5
     */
    public static byte toByte(String str) {
        return toByte(str, (byte) 0);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>byte</code>, returning a default
     * value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, the default value is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toByte(null, 1) = 1
     *   NumberUtils.toByte("", 1)   = 1
     *   NumberUtils.toByte("1", 0)  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @param defaultValue
     *            the default value
     * @return the byte represented by the string, or the default if conversion
     *         fails
     * @since 2.5
     */
    public static byte toByte(String str, byte defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Byte.parseByte(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>short</code>, returning
     * <code>zero</code> if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, <code>zero</code> is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toShort(null) = 0
     *   NumberUtils.toShort("")   = 0
     *   NumberUtils.toShort("1")  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @return the short represented by the string, or <code>zero</code> if
     *         conversion fails
     * @since 2.5
     */
    public static short toShort(String str) {
        return toShort(str, (short) 0);
    }

    /**
     * <p>
     * Convert a <code>String</code> to an <code>short</code>, returning a
     * default value if the conversion fails.
     * </p>
     * 
     * <p>
     * If the string is <code>null</code>, the default value is returned.
     * </p>
     * 
     * <pre>
     *   NumberUtils.toShort(null, 1) = 1
     *   NumberUtils.toShort("", 1)   = 1
     *   NumberUtils.toShort("1", 0)  = 1
     * </pre>
     * 
     * @param str
     *            the string to convert, may be null
     * @param defaultValue
     *            the default value
     * @return the short represented by the string, or the default if conversion
     *         fails
     * @since 2.5
     */
    public static short toShort(String str, short defaultValue) {
        if (str == null) {
            return defaultValue;
        }
        try {
            return Short.parseShort(str);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    }

    // -----------------------------------------------------------------------
    // must handle Long, Float, Integer, Float, Short,
    // BigDecimal, BigInteger and Byte
    // useful methods:
    // Byte.decode(String)
    // Byte.valueOf(String,int radix)
    // Byte.valueOf(String)
    // Double.valueOf(String)
    // Float.valueOf(String)
    // new Float(String)
    // Integer.valueOf(String,int radix)
    // Integer.valueOf(String)
    // Integer.decode(String)
    // Integer.getInteger(String)
    // Integer.getInteger(String,int val)
    // Integer.getInteger(String,Integer val)
    // new Integer(String)
    // new Double(String)
    // new Byte(String)
    // new Long(String)
    // Long.getLong(String)
    // Long.getLong(String,int)
    // Long.getLong(String,Integer)
    // Long.valueOf(String,int)
    // Long.valueOf(String)
    // new Short(String)
    // Short.decode(String)
    // Short.valueOf(String,int)
    // Short.valueOf(String)
    // new BigDecimal(String)
    // new BigInteger(String)
    // new BigInteger(String,int radix)
    // Possible inputs:
    // 45 45.5 45E7 4.5E7 Hex Oct Binary xxxF xxxD xxxf xxxd
    // plus minus everything. Prolly more. A lot are not separable.

    /**
     * <p>
     * Turns a string value into a java.lang.Number.
     * </p>
     * 
     * <p>
     * First, the value is examined for a type qualifier on the end (
     * <code>'f','F','d','D','l','L'</code>). If it is found, it starts trying
     * to create successively larger types from the type specified until one is
     * found that can represent the value.
     * </p>
     * 
     * <p>
     * If a type specifier is not found, it will check for a decimal point and
     * then try successively larger types from <code>Integer</code> to
     * <code>BigInteger</code> and from <code>Float</code> to
     * <code>BigDecimal</code>.
     * </p>
     * 
     * <p>
     * If the string starts with <code>0x</code> or <code>-0x</code>, it will be
     * interpreted as a hexadecimal integer. Values with leading <code>0</code>
     * 's will not be interpreted as octal.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * <p>
     * This method does not trim the input string, i.e., strings with leading or
     * trailing spaces will generate NumberFormatExceptions.
     * </p>
     * 
     * @param str
     *            String containing a number, may be null
     * @return Number created from the string
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static Number createNumber(String str) throws NumberFormatException {
        if (str == null) {
            return null;
        }
        if (isBlank(str)) {
            throw new NumberFormatException(
                    "A blank string is not a valid number");
        }
        if (str.startsWith("--")) {
            // this is protection for poorness in java.lang.BigDecimal.
            // it accepts this as a legal value, but it does not appear
            // to be in specification of class. OS X Java parses it to
            // a wrong value.
            return null;
        }
        if (str.startsWith("0x") || str.startsWith("-0x")) {
            return createInteger(str);
        }
        char lastChar = str.charAt(str.length() - 1);
        String mant;
        String dec;
        String exp;
        int decPos = str.indexOf('.');
        int expPos = str.indexOf('e') + str.indexOf('E') + 1;

        if (decPos > -1) {

            if (expPos > -1) {
                if (expPos < decPos) {
                    throw new NumberFormatException(str
                            + " is not a valid number.");
                }
                dec = str.substring(decPos + 1, expPos);
            } else {
                dec = str.substring(decPos + 1);
            }
            mant = str.substring(0, decPos);
        } else {
            if (expPos > -1) {
                mant = str.substring(0, expPos);
            } else {
                mant = str;
            }
            dec = null;
        }
        if (!Character.isDigit(lastChar) && lastChar != '.') {
            if (expPos > -1 && expPos < str.length() - 1) {
                exp = str.substring(expPos + 1, str.length() - 1);
            } else {
                exp = null;
            }
            // Requesting a specific type..
            String numeric = str.substring(0, str.length() - 1);
            boolean allZeros = isAllZeros(mant) && isAllZeros(exp);
            switch (lastChar) {
                case 'l':
                case 'L':
                    if (dec == null
                            && exp == null
                            && (numeric.charAt(0) == '-'
                                    && isDigits(numeric.substring(1)) || isDigits(numeric))) {
                        try {
                            return createLong(numeric);
                        } catch (NumberFormatException nfe) {
                            // Too big for a long
                        }
                        return createBigInteger(numeric);

                    }
                    throw new NumberFormatException(str
                            + " is not a valid number.");
                case 'f':
                case 'F':
                    try {
                        Float f = createFloat(numeric);
                        if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) {
                            // If it's too big for a float or the float value =
                            // 0
                            // and the string
                            // has non-zeros in it, then float does not have the
                            // precision we want
                            return f;
                        }

                    } catch (NumberFormatException nfe) {
                        // ignore the bad number
                    }
                    //$FALL-THROUGH$
                case 'd':
                case 'D':
                    try {
                        Double d = createDouble(numeric);
                        if (!(d.isInfinite() || (d.floatValue() == 0.0D && !allZeros))) {
                            return d;
                        }
                    } catch (NumberFormatException nfe) {
                        // ignore the bad number
                    }
                    try {
                        return createBigDecimal(numeric);
                    } catch (NumberFormatException e) {
                        // ignore the bad number
                    }
                    //$FALL-THROUGH$
                default:
                    throw new NumberFormatException(str
                            + " is not a valid number.");

            }
        } else {
            // User doesn't have a preference on the return type, so let's start
            // small and go from there...
            if (expPos > -1 && expPos < str.length() - 1) {
                exp = str.substring(expPos + 1, str.length());
            } else {
                exp = null;
            }
            if (dec == null && exp == null) {
                // Must be an int,long,bigint
                try {
                    return createInteger(str);
                } catch (NumberFormatException nfe) {
                    // ignore the bad number
                }
                try {
                    return createLong(str);
                } catch (NumberFormatException nfe) {
                    // ignore the bad number
                }
                return createBigInteger(str);

            } else {
                // Must be a float,double,BigDec
                boolean allZeros = isAllZeros(mant) && isAllZeros(exp);
                try {
                    Float f = createFloat(str);
                    if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) {
                        return f;
                    }
                } catch (NumberFormatException nfe) {
                    // ignore the bad number
                }
                try {
                    Double d = createDouble(str);
                    if (!(d.isInfinite() || (d.doubleValue() == 0.0D && !allZeros))) {
                        return d;
                    }
                } catch (NumberFormatException nfe) {
                    // ignore the bad number
                }

                return createBigDecimal(str);

            }
        }
    }

    /**
     * <p>
     * Utility method for {@link #createNumber(java.lang.String)}.
     * </p>
     * 
     * <p>
     * Returns <code>true</code> if s is <code>null</code>.
     * </p>
     * 
     * @param str
     *            the String to check
     * @return if it is all zeros or <code>null</code>
     */
    private static boolean isAllZeros(String str) {
        if (str == null) {
            return true;
        }
        for (int i = str.length() - 1; i >= 0; i--) {
            if (str.charAt(i) != '0') {
                return false;
            }
        }
        return str.length() > 0;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Convert a <code>String</code> to a <code>Float</code>.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>Float</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static Float createFloat(String str) {
        if (str == null) {
            return null;
        }
        return Float.valueOf(str);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>Double</code>.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>Double</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static Double createDouble(String str) {
        if (str == null) {
            return null;
        }
        return Double.valueOf(str);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>Integer</code>, handling hex and
     * octal notations.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>Integer</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static Integer createInteger(String str) {
        if (str == null) {
            return null;
        }
        // decode() handles 0xAABD and 0777 (hex and octal) as well.
        return Integer.decode(str);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>Long</code>.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>Long</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static Long createLong(String str) {
        if (str == null) {
            return null;
        }
        return Long.valueOf(str);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>BigInteger</code>.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>BigInteger</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static BigInteger createBigInteger(String str) {
        if (str == null) {
            return null;
        }
        return new BigInteger(str);
    }

    /**
     * <p>
     * Convert a <code>String</code> to a <code>BigDecimal</code>.
     * </p>
     * 
     * <p>
     * Returns <code>null</code> if the string is <code>null</code>.
     * </p>
     * 
     * @param str
     *            a <code>String</code> to convert, may be null
     * @return converted <code>BigDecimal</code>
     * @throws NumberFormatException
     *             if the value cannot be converted
     */
    public static BigDecimal createBigDecimal(String str) {
        if (str == null) {
            return null;
        }
        // handle JDK1.3.1 bug where "" throws IndexOutOfBoundsException
        if (isBlank(str)) {
            throw new NumberFormatException(
                    "A blank string is not a valid number");
        }
        return new BigDecimal(str);
    }

    // Min in array
    // --------------------------------------------------------------------
    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static long min(long[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        long min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static int min(int[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        int min = array[0];
        for (int j = 1; j < array.length; j++) {
            if (array[j] < min) {
                min = array[j];
            }
        }

        return min;
    }

    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static short min(short[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        short min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static byte min(byte[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        byte min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     * @see IEEE754rUtils#min(double[]) IEEE754rUtils for a version of this
     *      method that handles NaN differently
     */
    public static double min(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        double min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (Double.isNaN(array[i])) {
                return Double.NaN;
            }
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    /**
     * <p>
     * Returns the minimum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     * @see IEEE754rUtils#min(float[]) IEEE754rUtils for a version of this
     *      method that handles NaN differently
     */
    public static float min(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns min
        float min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (Float.isNaN(array[i])) {
                return Float.NaN;
            }
            if (array[i] < min) {
                min = array[i];
            }
        }

        return min;
    }

    // Max in array
    // --------------------------------------------------------------------
    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static long max(long[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        long max = array[0];
        for (int j = 1; j < array.length; j++) {
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static int max(int[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        int max = array[0];
        for (int j = 1; j < array.length; j++) {
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static short max(short[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        short max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }

        return max;
    }

    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     */
    public static byte max(byte[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        byte max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }

        return max;
    }

    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     * @see IEEE754rUtils#max(double[]) IEEE754rUtils for a version of this
     *      method that handles NaN differently
     */
    public static double max(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        double max = array[0];
        for (int j = 1; j < array.length; j++) {
            if (Double.isNaN(array[j])) {
                return Double.NaN;
            }
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    /**
     * <p>
     * Returns the maximum value in an array.
     * </p>
     * 
     * @param array
     *            an array, must not be null or empty
     * @return the minimum value in the array
     * @throws IllegalArgumentException
     *             if <code>array</code> is <code>null</code>
     * @throws IllegalArgumentException
     *             if <code>array</code> is empty
     * @see IEEE754rUtils#max(float[]) IEEE754rUtils for a version of this
     *      method that handles NaN differently
     */
    public static float max(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        float max = array[0];
        for (int j = 1; j < array.length; j++) {
            if (Float.isNaN(array[j])) {
                return Float.NaN;
            }
            if (array[j] > max) {
                max = array[j];
            }
        }

        return max;
    }

    // 3 param min
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the minimum of three <code>long</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     */
    public static long min(long a, long b, long c) {
        if (b < a) {
            a = b;
        }
        if (c < a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the minimum of three <code>int</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     */
    public static int min(int a, int b, int c) {
        if (b < a) {
            a = b;
        }
        if (c < a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the minimum of three <code>short</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     */
    public static short min(short a, short b, short c) {
        if (b < a) {
            a = b;
        }
        if (c < a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the minimum of three <code>byte</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     */
    public static byte min(byte a, byte b, byte c) {
        if (b < a) {
            a = b;
        }
        if (c < a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the minimum of three <code>double</code> values.
     * </p>
     * 
     * <p>
     * If any value is <code>NaN</code>, <code>NaN</code> is returned. Infinity
     * is handled.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     * @see IEEE754rUtils#min(double, double, double) for a version of this
     *      method that handles NaN differently
     */
    public static double min(double a, double b, double c) {
        return Math.min(Math.min(a, b), c);
    }

    /**
     * <p>
     * Gets the minimum of three <code>float</code> values.
     * </p>
     * 
     * <p>
     * If any value is <code>NaN</code>, <code>NaN</code> is returned. Infinity
     * is handled.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the smallest of the values
     * @see IEEE754rUtils#min(float, float, float) for a version of this method
     *      that handles NaN differently
     */
    public static float min(float a, float b, float c) {
        return Math.min(Math.min(a, b), c);
    }

    // 3 param max
    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the maximum of three <code>long</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     */
    public static long max(long a, long b, long c) {
        if (b > a) {
            a = b;
        }
        if (c > a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the maximum of three <code>int</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     */
    public static int max(int a, int b, int c) {
        if (b > a) {
            a = b;
        }
        if (c > a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the maximum of three <code>short</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     */
    public static short max(short a, short b, short c) {
        if (b > a) {
            a = b;
        }
        if (c > a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the maximum of three <code>byte</code> values.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     */
    public static byte max(byte a, byte b, byte c) {
        if (b > a) {
            a = b;
        }
        if (c > a) {
            a = c;
        }
        return a;
    }

    /**
     * <p>
     * Gets the maximum of three <code>double</code> values.
     * </p>
     * 
     * <p>
     * If any value is <code>NaN</code>, <code>NaN</code> is returned. Infinity
     * is handled.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     * @see IEEE754rUtils#max(double, double, double) for a version of this
     *      method that handles NaN differently
     */
    public static double max(double a, double b, double c) {
        return Math.max(Math.max(a, b), c);
    }

    /**
     * <p>
     * Gets the maximum of three <code>float</code> values.
     * </p>
     * 
     * <p>
     * If any value is <code>NaN</code>, <code>NaN</code> is returned. Infinity
     * is handled.
     * </p>
     * 
     * @param a
     *            value 1
     * @param b
     *            value 2
     * @param c
     *            value 3
     * @return the largest of the values
     * @see IEEE754rUtils#max(float, float, float) for a version of this method
     *      that handles NaN differently
     */
    public static float max(float a, float b, float c) {
        return Math.max(Math.max(a, b), c);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks whether the <code>String</code> contains only digit characters.
     * </p>
     * 
     * <p>
     * <code>Null</code> and empty String will return <code>false</code>.
     * </p>
     * 
     * @param str
     *            the <code>String</code> to check
     * @return <code>true</code> if str contains only unicode numeric
     */
    public static boolean isDigits(String str) {
        if (isEmpty(str)) {
            return false;
        }
        for (int i = 0; i < str.length(); i++) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * <p>
     * Checks whether the String a valid Java number.
     * </p>
     * 
     * <p>
     * Valid numbers include hexadecimal marked with the <code>0x</code>
     * qualifier, scientific notation and numbers marked with a type qualifier
     * (e.g. 123L).
     * </p>
     * 
     * <p>
     * <code>Null</code> and empty String will return <code>false</code>.
     * </p>
     * 
     * @param str
     *            the <code>String</code> to check
     * @return <code>true</code> if the string is a correctly formatted number
     */
    public static boolean isNumber(String str) {
        if (isEmpty(str)) {
            return false;
        }
        char[] chars = str.toCharArray();
        int sz = chars.length;
        boolean hasExp = false;
        boolean hasDecPoint = false;
        boolean allowSigns = false;
        boolean foundDigit = false;
        // deal with any possible sign up front
        int start = (chars[0] == '-') ? 1 : 0;
        if (sz > start + 1) {
            if (chars[start] == '0' && chars[start + 1] == 'x') {
                int i = start + 2;
                if (i == sz) {
                    return false; // str == "0x"
                }
                // checking hex (it can't be anything else)
                for (; i < chars.length; i++) {
                    if ((chars[i] < '0' || chars[i] > '9')
                            && (chars[i] < 'a' || chars[i] > 'f')
                            && (chars[i] < 'A' || chars[i] > 'F')) {
                        return false;
                    }
                }
                return true;
            }
        }
        sz--; // don't want to loop to the last char, check it afterwords
        // for type qualifiers
        int i = start;
        // loop to the next to last char or to the last char if we need another
        // digit to
        // make a valid number (e.g. chars[0..5] = "1234E")
        while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
            if (chars[i] >= '0' && chars[i] <= '9') {
                foundDigit = true;
                allowSigns = false;

            } else if (chars[i] == '.') {
                if (hasDecPoint || hasExp) {
                    // two decimal points or dec in exponent
                    return false;
                }
                hasDecPoint = true;
            } else if (chars[i] == 'e' || chars[i] == 'E') {
                // we've already taken care of hex.
                if (hasExp) {
                    // two E's
                    return false;
                }
                if (!foundDigit) {
                    return false;
                }
                hasExp = true;
                allowSigns = true;
            } else if (chars[i] == '+' || chars[i] == '-') {
                if (!allowSigns) {
                    return false;
                }
                allowSigns = false;
                foundDigit = false; // we need a digit after the E
            } else {
                return false;
            }
            i++;
        }
        if (i < chars.length) {
            if (chars[i] >= '0' && chars[i] <= '9') {
                // no type qualifier, OK
                return true;
            }
            if (chars[i] == 'e' || chars[i] == 'E') {
                // can't have an E at the last byte
                return false;
            }
            if (chars[i] == '.') {
                if (hasDecPoint || hasExp) {
                    // two decimal points or dec in exponent
                    return false;
                }
                // single trailing decimal point after non-exponent is ok
                return foundDigit;
            }
            if (!allowSigns
                    && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) {
                return foundDigit;
            }
            if (chars[i] == 'l' || chars[i] == 'L') {
                // not allowing L with an exponent
                return foundDigit && !hasExp;
            }
            // last character is illegal
            return false;
        }
        // allowSigns is true iff the val ends in 'E'
        // found digit it to make sure weird stuff like '.' and '1E-' doesn't
        // pass
        return !allowSigns && foundDigit;
    }

    // ///////////////////////////////////////////
    //
    // ConstructorUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Returns new instance of <code>klazz</code> created using the actual
     * arguments <code>args</code>. The formal parameter types are inferred from
     * the actual values of <code>args</code>. See
     * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
     * details.
     * </p>
     * 
     * <p>
     * The signatures should be assignment compatible.
     * </p>
     * 
     * @param cls
     *            the class to be constructed.
     * @param args
     *            actual argument array
     * @return new instance of <code>klazz</code>
     * 
     * @throws NoSuchMethodException
     *             If the constructor cannot be found
     * @throws IllegalAccessException
     *             If an error occurs accessing the constructor
     * @throws InvocationTargetException
     *             If an error occurs invoking the constructor
     * @throws InstantiationException
     *             If an error occurs instantiating the class
     * 
     * @see #invokeConstructor(java.lang.Class, java.lang.Object[],
     *      java.lang.Class[])
     */
    public static <T> T invokeConstructor(Class<T> cls, Object... args)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        if (null == args) {
            args = EMPTY_OBJECT_ARRAY;
        }
        Class<?> parameterTypes[] = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeConstructor(cls, args, parameterTypes);
    }

    /**
     * <p>
     * Returns new instance of <code>klazz</code> created using constructor with
     * signature <code>parameterTypes</code> and actual arguments
     * <code>args</code>.
     * </p>
     * 
     * <p>
     * The signatures should be assignment compatible.
     * </p>
     * 
     * @param cls
     *            the class to be constructed.
     * @param args
     *            actual argument array
     * @param parameterTypes
     *            parameter types array
     * @return new instance of <code>klazz</code>
     * 
     * @throws NoSuchMethodException
     *             if matching constructor cannot be found
     * @throws IllegalAccessException
     *             thrown on the constructor's invocation
     * @throws InvocationTargetException
     *             thrown on the constructor's invocation
     * @throws InstantiationException
     *             thrown on the constructor's invocation
     * @see Constructor#newInstance
     */
    public static <T> T invokeConstructor(Class<T> cls, Object[] args,
            Class<?>[] parameterTypes) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException,
            InstantiationException {
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        Constructor<T> ctor = getMatchingAccessibleConstructor(cls,
                parameterTypes);
        if (null == ctor) {
            throw new NoSuchMethodException(
                    "No such accessible constructor on object: "
                            + cls.getName());
        }
        return ctor.newInstance(args);
    }

    /**
     * <p>
     * Returns new instance of <code>klazz</code> created using the actual
     * arguments <code>args</code>. The formal parameter types are inferred from
     * the actual values of <code>args</code>. See
     * {@link #invokeExactConstructor(Class, Object[], Class[])} for more
     * details.
     * </p>
     * 
     * <p>
     * The signatures should match exactly.
     * </p>
     * 
     * @param cls
     *            the class to be constructed.
     * @param args
     *            actual argument array
     * @return new instance of <code>klazz</code>
     * 
     * @throws NoSuchMethodException
     *             If the constructor cannot be found
     * @throws IllegalAccessException
     *             If an error occurs accessing the constructor
     * @throws InvocationTargetException
     *             If an error occurs invoking the constructor
     * @throws InstantiationException
     *             If an error occurs instantiating the class
     * 
     * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[],
     *      java.lang.Class[])
     */
    public static <T> T invokeExactConstructor(Class<T> cls, Object... args)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        if (null == args) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?> parameterTypes[] = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeExactConstructor(cls, args, parameterTypes);
    }

    /**
     * <p>
     * Returns new instance of <code>klazz</code> created using constructor with
     * signature <code>parameterTypes</code> and actual arguments
     * <code>args</code>.
     * </p>
     * 
     * <p>
     * The signatures should match exactly.
     * </p>
     * 
     * @param cls
     *            the class to be constructed.
     * @param args
     *            actual argument array
     * @param parameterTypes
     *            parameter types array
     * @return new instance of <code>klazz</code>
     * 
     * @throws NoSuchMethodException
     *             if matching constructor cannot be found
     * @throws IllegalAccessException
     *             thrown on the constructor's invocation
     * @throws InvocationTargetException
     *             thrown on the constructor's invocation
     * @throws InstantiationException
     *             thrown on the constructor's invocation
     * @see Constructor#newInstance
     */
    public static <T> T invokeExactConstructor(Class<T> cls, Object[] args,
            Class<?>[] parameterTypes) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException,
            InstantiationException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        Constructor<T> ctor = getAccessibleConstructor(cls, parameterTypes);
        if (null == ctor) {
            throw new NoSuchMethodException(
                    "No such accessible constructor on object: "
                            + cls.getName());
        }
        return ctor.newInstance(args);
    }

    /**
     * Returns a constructor given a class and signature.
     * 
     * @param cls
     *            the class to be constructed
     * @param parameterTypes
     *            the parameter array
     * @return null if matching accessible constructor can not be found
     * @see Class#getConstructor
     * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
     */
    public static <T> Constructor<T> getAccessibleConstructor(Class<T> cls,
            Class<?>... parameterTypes) {
        try {
            return getAccessibleConstructor(cls.getConstructor(parameterTypes));
        } catch (NoSuchMethodException e) {
            return (null);
        }
    }

    /**
     * Returns accessible version of the given constructor.
     * 
     * @param ctor
     *            prototype constructor object.
     * @return <code>null</code> if accessible constructor can not be found.
     * @see java.lang.SecurityManager
     */
    public static <T> Constructor<T> getAccessibleConstructor(
            Constructor<T> ctor) {
        return isAccessible(ctor)
                && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor
                : null;
    }

    /**
     * <p>
     * Find an accessible constructor with compatible parameters. Compatible
     * parameters mean that every method parameter is assignable from the given
     * parameters. In other words, it finds constructor that will take the
     * parameters given.
     * </p>
     * 
     * <p>
     * First it checks if there is constructor matching the exact signature. If
     * no such, all the constructors of the class are tested if their signatures
     * are assignment compatible with the parameter types. The first matching
     * constructor is returned.
     * </p>
     * 
     * @param cls
     *            find constructor for this class
     * @param parameterTypes
     *            find method with compatible parameters
     * @return a valid Constructor object. If there's no matching constructor,
     *         returns <code>null</code>.
     */
    @SuppressWarnings("unchecked")
    public static <T> Constructor<T> getMatchingAccessibleConstructor(
            Class<T> cls, Class<?>... parameterTypes) {
        // see if we can find the constructor directly
        // most of the time this works and it's much faster
        try {
            Constructor<T> ctor = cls.getConstructor(parameterTypes);
            setAccessibleWorkaround(ctor);
            return ctor;
        } catch (NoSuchMethodException e) { /* SWALLOW */
        }
        Constructor<T> result = null;
        // search through all constructors
        Constructor<?>[] ctors = cls.getConstructors();
        for (int i = 0; i < ctors.length; i++) {
            // compare parameters
            if (isAssignable(parameterTypes, ctors[i].getParameterTypes(), true)) {
                // get accessible version of method
                Constructor<T> ctor = getAccessibleConstructor((Constructor<T>) ctors[i]);
                if (ctor != null) {
                    setAccessibleWorkaround(ctor);
                    if (result == null
                            || compareParameterTypes(ctor.getParameterTypes(),
                                    result.getParameterTypes(), parameterTypes) < 0) {
                        result = ctor;
                    }
                }
            }
        }
        return result;
    }

    // ///////////////////////////////////////////
    //
    // MethodUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Invoke a named method whose parameter type matches the object type.
     * </p>
     * 
     * <p>
     * This method delegates the method search to
     * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * object would match a <code>boolean</code> primitive.
     * </p>
     * 
     * <p>
     * This is a convenient wrapper for
     * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}
     * .
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeMethod(Object object, String methodName,
            Object... args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?>[] parameterTypes = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeMethod(object, methodName, args, parameterTypes);
    }

    /**
     * <p>
     * Invoke a named method whose parameter type matches the object type.
     * </p>
     * 
     * <p>
     * This method delegates the method search to
     * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * object would match a <code>boolean</code> primitive.
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @param parameterTypes
     *            match these parameters - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeMethod(Object object, String methodName,
            Object[] args, Class<?>[] parameterTypes)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        Method method = getMatchingAccessibleMethod(object.getClass(),
                methodName, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException("No such accessible method: "
                    + methodName + "() on object: "
                    + object.getClass().getName());
        }
        return method.invoke(object, args);
    }

    /**
     * <p>
     * Invoke a method whose parameter types match exactly the object types.
     * </p>
     * 
     * <p>
     * This uses reflection to invoke the method obtained from a call to
     * <code>getAccessibleMethod()</code>.
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeExactMethod(Object object, String methodName,
            Object... args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?>[] parameterTypes = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeExactMethod(object, methodName, args, parameterTypes);
    }

    /**
     * <p>
     * Invoke a method whose parameter types match exactly the parameter types
     * given.
     * </p>
     * 
     * <p>
     * This uses reflection to invoke the method obtained from a call to
     * <code>getAccessibleMethod()</code>.
     * </p>
     * 
     * @param object
     *            invoke method on this object
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @param parameterTypes
     *            match these parameters - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeExactMethod(Object object, String methodName,
            Object[] args, Class<?>[] parameterTypes)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        Method method = getAccessibleMethod(object.getClass(), methodName,
                parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException("No such accessible method: "
                    + methodName + "() on object: "
                    + object.getClass().getName());
        }
        return method.invoke(object, args);
    }

    /**
     * <p>
     * Invoke a static method whose parameter types match exactly the parameter
     * types given.
     * </p>
     * 
     * <p>
     * This uses reflection to invoke the method obtained from a call to
     * {@link #getAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * @param cls
     *            invoke static method on this class
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @param parameterTypes
     *            match these parameters - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeExactStaticMethod(Class<?> cls,
            String methodName, Object[] args, Class<?>[] parameterTypes)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        Method method = getAccessibleMethod(cls, methodName, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException("No such accessible method: "
                    + methodName + "() on class: " + cls.getName());
        }
        return method.invoke(null, args);
    }

    /**
     * <p>
     * Invoke a named static method whose parameter type matches the object
     * type.
     * </p>
     * 
     * <p>
     * This method delegates the method search to
     * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * class would match a <code>boolean</code> primitive.
     * </p>
     * 
     * <p>
     * This is a convenient wrapper for
     * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}
     * .
     * </p>
     * 
     * @param cls
     *            invoke static method on this class
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeStaticMethod(Class<?> cls, String methodName,
            Object... args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?>[] parameterTypes = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeStaticMethod(cls, methodName, args, parameterTypes);
    }

    /**
     * <p>
     * Invoke a named static method whose parameter type matches the object
     * type.
     * </p>
     * 
     * <p>
     * This method delegates the method search to
     * {@link #getMatchingAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * <p>
     * This method supports calls to methods taking primitive parameters via
     * passing in wrapping classes. So, for example, a <code>Boolean</code>
     * class would match a <code>boolean</code> primitive.
     * </p>
     * 
     * 
     * @param cls
     *            invoke static method on this class
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @param parameterTypes
     *            match these parameters - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeStaticMethod(Class<?> cls, String methodName,
            Object[] args, Class<?>[] parameterTypes)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_ARRAY;
        }
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        Method method = getMatchingAccessibleMethod(cls, methodName,
                parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException("No such accessible method: "
                    + methodName + "() on class: " + cls.getName());
        }
        return method.invoke(null, args);
    }

    /**
     * <p>
     * Invoke a static method whose parameter types match exactly the object
     * types.
     * </p>
     * 
     * <p>
     * This uses reflection to invoke the method obtained from a call to
     * {@link #getAccessibleMethod(Class, String, Class[])}.
     * </p>
     * 
     * @param cls
     *            invoke static method on this class
     * @param methodName
     *            get method with this name
     * @param args
     *            use these arguments - treat null as empty array
     * @return The value returned by the invoked method
     * 
     * @throws NoSuchMethodException
     *             if there is no such accessible method
     * @throws InvocationTargetException
     *             wraps an exception thrown by the method invoked
     * @throws IllegalAccessException
     *             if the requested method is not accessible via reflection
     */
    public static Object invokeExactStaticMethod(Class<?> cls,
            String methodName, Object... args) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?>[] parameterTypes = new Class[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeExactStaticMethod(cls, methodName, args, parameterTypes);
    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) with given name and parameters. If no such method can be
     * found, return <code>null</code>. This is just a convenient wrapper for
     * {@link #getAccessibleMethod(Method method)}.
     * </p>
     * 
     * @param cls
     *            get method from this class
     * @param methodName
     *            get method with this name
     * @param parameterTypes
     *            with these parameters types
     * @return The accessible method
     */
    public static Method getAccessibleMethod(Class<?> cls, String methodName,
            Class<?>... parameterTypes) {
        try {
            return getAccessibleMethod(cls
                    .getMethod(methodName, parameterTypes));
        } catch (NoSuchMethodException e) {
            return (null);
        }
    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) that implements the specified Method. If no such method can
     * be found, return <code>null</code>.
     * </p>
     * 
     * @param method
     *            The method that we wish to call
     * @return The accessible method
     */
    public static Method getAccessibleMethod(Method method) {
        if (!isAccessible(method)) {
            return null;
        }
        // If the declaring class is public, we are done
        Class<?> cls = method.getDeclaringClass();
        if (Modifier.isPublic(cls.getModifiers())) {
            return method;
        }
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();

        // Check the implemented interfaces and subinterfaces
        method = getAccessibleMethodFromInterfaceNest(cls, methodName,
                parameterTypes);

        // Check the superclass chain
        if (method == null) {
            method = getAccessibleMethodFromSuperclass(cls, methodName,
                    parameterTypes);
        }
        return method;
    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) by scanning through the superclasses. If no such method can
     * be found, return <code>null</code>.
     * </p>
     * 
     * @param cls
     *            Class to be checked
     * @param methodName
     *            Method name of the method we wish to call
     * @param parameterTypes
     *            The parameter type signatures
     * @return the accessible method or <code>null</code> if not found
     */
    private static Method getAccessibleMethodFromSuperclass(Class<?> cls,
            String methodName, Class<?>... parameterTypes) {
        Class<?> parentClass = cls.getSuperclass();
        while (parentClass != null) {
            if (Modifier.isPublic(parentClass.getModifiers())) {
                try {
                    return parentClass.getMethod(methodName, parameterTypes);
                } catch (NoSuchMethodException e) {
                    return null;
                }
            }
            parentClass = parentClass.getSuperclass();
        }
        return null;
    }

    /**
     * <p>
     * Return an accessible method (that is, one that can be invoked via
     * reflection) that implements the specified method, by scanning through all
     * implemented interfaces and subinterfaces. If no such method can be found,
     * return <code>null</code>.
     * </p>
     * 
     * <p>
     * There isn't any good reason why this method must be private. It is
     * because there doesn't seem any reason why other classes should call this
     * rather than the higher level methods.
     * </p>
     * 
     * @param cls
     *            Parent class for the interfaces to be checked
     * @param methodName
     *            Method name of the method we wish to call
     * @param parameterTypes
     *            The parameter type signatures
     * @return the accessible method or <code>null</code> if not found
     */
    private static Method getAccessibleMethodFromInterfaceNest(Class<?> cls,
            String methodName, Class<?>... parameterTypes) {
        Method method = null;

        // Search up the superclass chain
        for (; cls != null; cls = cls.getSuperclass()) {

            // Check the implemented interfaces of the parent class
            Class<?>[] interfaces = cls.getInterfaces();
            for (int i = 0; i < interfaces.length; i++) {
                // Is this interface public?
                if (!Modifier.isPublic(interfaces[i].getModifiers())) {
                    continue;
                }
                // Does the method exist on this interface?
                try {
                    method = interfaces[i].getDeclaredMethod(methodName,
                            parameterTypes);
                } catch (NoSuchMethodException e) {
                    /*
                     * Swallow, if no method is found after the loop then this
                     * method returns null.
                     */
                }
                if (method != null) {
                    break;
                }
                // Recursively check our parent interfaces
                method = getAccessibleMethodFromInterfaceNest(interfaces[i],
                        methodName, parameterTypes);
                if (method != null) {
                    break;
                }
            }
        }
        return method;
    }

    /**
     * <p>
     * Find an accessible method that matches the given name and has compatible
     * parameters. Compatible parameters mean that every method parameter is
     * assignable from the given parameters. In other words, it finds a method
     * with the given name that will take the parameters given.
     * <p>
     * 
     * <p>
     * This method is used by
     * {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
     * 
     * <p>
     * This method can match primitive parameter by passing in wrapper classes.
     * For example, a <code>Boolean</code> will match a primitive
     * <code>boolean</code> parameter.
     * 
     * @param cls
     *            find method in this class
     * @param methodName
     *            find method with this name
     * @param parameterTypes
     *            find method with most compatible parameters
     * @return The accessible method
     */
    public static Method getMatchingAccessibleMethod(Class<?> cls,
            String methodName, Class<?>... parameterTypes) {
        try {
            Method method = cls.getMethod(methodName, parameterTypes);
            setAccessibleWorkaround(method);
            return method;
        } catch (NoSuchMethodException e) { /* SWALLOW */
        }
        // search through all methods
        Method bestMatch = null;
        Method[] methods = cls.getMethods();
        for (int i = 0, size = methods.length; i < size; i++) {
            if (methods[i].getName().equals(methodName)) {
                // compare parameters
                if (isAssignable(parameterTypes,
                        methods[i].getParameterTypes(), true)) {
                    // get accessible version of method
                    Method accessibleMethod = getAccessibleMethod(methods[i]);
                    if (accessibleMethod != null) {
                        if (bestMatch == null
                                || compareParameterTypes(
                                        accessibleMethod.getParameterTypes(),
                                        bestMatch.getParameterTypes(),
                                        parameterTypes) < 0) {
                            bestMatch = accessibleMethod;
                        }
                    }
                }
            }
        }
        if (bestMatch != null) {
            setAccessibleWorkaround(bestMatch);
        }
        return bestMatch;
    }

    // ///////////////////////////////////////////
    //
    // MemberUtils
    //
    // ///////////////////////////////////////////

    private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED
            | Modifier.PRIVATE;

    /** Array of primitive number types ordered by "promotability" */
    private static final Class<?>[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE,
            Short.TYPE, Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE,
            Double.TYPE };

    /**
     * XXX Default access superclass workaround
     * 
     * When a public class has a default access superclass with public members,
     * these members are accessible. Calling them from compiled code works fine.
     * Unfortunately, on some JVMs, using reflection to invoke these members
     * seems to (wrongly) to prevent access even when the modifer is public.
     * Calling setAccessible(true) solves the problem but will only work from
     * sufficiently privileged code. Better workarounds would be gratefully
     * accepted.
     * 
     * @param o
     *            the AccessibleObject to set as accessible
     */
    static void setAccessibleWorkaround(AccessibleObject o) {
        if (o == null || o.isAccessible()) {
            return;
        }
        Member m = (Member) o;
        if (Modifier.isPublic(m.getModifiers())
                && isPackageAccess(m.getDeclaringClass().getModifiers())) {
            try {
                o.setAccessible(true);
            } catch (SecurityException e) {
                // ignore in favor of subsequent IllegalAccessException
            }
        }
    }

    /**
     * Learn whether a given set of modifiers implies package access.
     * 
     * @param modifiers
     *            to test
     * @return true unless package/protected/private modifier detected
     */
    static boolean isPackageAccess(int modifiers) {
        return (modifiers & ACCESS_TEST) == 0;
    }

    /**
     * Check a Member for basic accessibility.
     * 
     * @param m
     *            Member to check
     * @return true if <code>m</code> is accessible
     */
    static boolean isAccessible(Member m) {
        return m != null && Modifier.isPublic(m.getModifiers())
                && !m.isSynthetic();
    }

    /**
     * Compare the relative fitness of two sets of parameter types in terms of
     * matching a third set of runtime parameter types, such that a list ordered
     * by the results of the comparison would return the best match first
     * (least).
     * 
     * @param left
     *            the "left" parameter set
     * @param right
     *            the "right" parameter set
     * @param actual
     *            the runtime parameter types to match against <code>left</code>
     *            /<code>right</code>
     * @return int consistent with <code>compare</code> semantics
     */
    static int compareParameterTypes(Class<?>[] left, Class<?>[] right,
            Class<?>[] actual) {
        float leftCost = getTotalTransformationCost(actual, left);
        float rightCost = getTotalTransformationCost(actual, right);
        return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0;
    }

    /**
     * Returns the sum of the object transformation cost for each class in the
     * source argument list.
     * 
     * @param srcArgs
     *            The source arguments
     * @param destArgs
     *            The destination arguments
     * @return The total transformation cost
     */
    private static float getTotalTransformationCost(Class<?>[] srcArgs,
            Class<?>[] destArgs) {
        float totalCost = 0.0f;
        for (int i = 0; i < srcArgs.length; i++) {
            Class<?> srcClass, destClass;
            srcClass = srcArgs[i];
            destClass = destArgs[i];
            totalCost += getObjectTransformationCost(srcClass, destClass);
        }
        return totalCost;
    }

    /**
     * Gets the number of steps required needed to turn the source class into
     * the destination class. This represents the number of steps in the object
     * hierarchy graph.
     * 
     * @param srcClass
     *            The source class
     * @param destClass
     *            The destination class
     * @return The cost of transforming an object
     */
    private static float getObjectTransformationCost(Class<?> srcClass,
            Class<?> destClass) {
        if (destClass.isPrimitive()) {
            return getPrimitivePromotionCost(srcClass, destClass);
        }
        float cost = 0.0f;
        while (destClass != null && !destClass.equals(srcClass)) {
            if (destClass.isInterface() && isAssignable(srcClass, destClass)) {
                // slight penalty for interface match.
                // we still want an exact match to override an interface match,
                // but
                // an interface match should override anything where we have to
                // get a superclass.
                cost += 0.25f;
                break;
            }
            cost++;
            destClass = destClass.getSuperclass();
        }
        /*
         * If the destination class is null, we've travelled all the way up to
         * an Object match. We'll penalize this by adding 1.5 to the cost.
         */
        if (destClass == null) {
            cost += 1.5f;
        }
        return cost;
    }

    /**
     * Get the number of steps required to promote a primitive number to another
     * type.
     * 
     * @param srcClass
     *            the (primitive) source class
     * @param destClass
     *            the (primitive) destination class
     * @return The cost of promoting the primitive
     */
    private static float getPrimitivePromotionCost(final Class<?> srcClass,
            final Class<?> destClass) {
        float cost = 0.0f;
        Class<?> cls = srcClass;
        if (!cls.isPrimitive()) {
            // slight unwrapping penalty
            cost += 0.1f;
            cls = wrapperToPrimitive(cls);
        }
        for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) {
            if (cls == ORDERED_PRIMITIVE_TYPES[i]) {
                cost += 0.1f;
                if (i < ORDERED_PRIMITIVE_TYPES.length - 1) {
                    cls = ORDERED_PRIMITIVE_TYPES[i + 1];
                }
            }
        }
        return cost;
    }

    // ///////////////////////////////////////////
    //
    // FieldUtils
    //
    // ///////////////////////////////////////////

    /**
     * Gets an accessible <code>Field</code> by name respecting scope.
     * Superclasses/interfaces will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     */
    public static Field getField(Class<?> cls, String fieldName) {
        Field field = getField(cls, fieldName, false);
        setAccessibleWorkaround(field);
        return field;
    }

    /**
     * Gets an accessible <code>Field</code> by name breaking scope if
     * requested. Superclasses/interfaces will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     */
    public static Field getField(final Class<?> cls, String fieldName,
            boolean forceAccess) {
        if (cls == null) {
            throw new IllegalArgumentException("The class must not be null");
        }
        if (fieldName == null) {
            throw new IllegalArgumentException(
                    "The field name must not be null");
        }
        // Sun Java 1.3 has a bugged implementation of getField hence we write
        // the
        // code ourselves

        // getField() will return the Field object with the declaring class
        // set correctly to the class that declares the field. Thus requesting
        // the
        // field on a subclass will return the field from the superclass.
        //
        // priority order for lookup:
        // searchclass private/protected/package/public
        // superclass protected/package/public
        // private/different package blocks access to further superclasses
        // implementedinterface public

        // check up the superclass hierarchy
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                Field field = acls.getDeclaredField(fieldName);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                if (!Modifier.isPublic(field.getModifiers())) {
                    if (forceAccess) {
                        field.setAccessible(true);
                    } else {
                        continue;
                    }
                }
                return field;
            } catch (NoSuchFieldException ex) {
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a
        // private/package
        // superclass field.
        Field match = null;
        for (Iterator<Class<?>> intf = getAllInterfaces(cls).iterator(); intf
                .hasNext();) {
            try {
                Field test = ((Class<?>) intf.next()).getField(fieldName);
                if (match != null) {
                    throw new IllegalArgumentException(
                            "Reference to field "
                                    + fieldName
                                    + " is ambiguous relative to "
                                    + cls
                                    + "; a matching field exists on two or more implemented interfaces.");
                }
                match = test;
            } catch (NoSuchFieldException ex) {
                // ignore
            }
        }
        return match;
    }

    /**
     * Gets an accessible <code>Field</code> by name respecting scope. Only the
     * specified class will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     */
    public static Field getDeclaredField(Class<?> cls, String fieldName) {
        return getDeclaredField(cls, fieldName, false);
    }

    /**
     * Gets an accessible <code>Field</code> by name breaking scope if
     * requested. Only the specified class will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. False will only match
     *            public fields.
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     */
    public static Field getDeclaredField(Class<?> cls, String fieldName,
            boolean forceAccess) {
        if (cls == null) {
            throw new IllegalArgumentException("The class must not be null");
        }
        if (fieldName == null) {
            throw new IllegalArgumentException(
                    "The field name must not be null");
        }
        try {
            // only consider the specified class by using getDeclaredField()
            Field field = cls.getDeclaredField(fieldName);
            if (!isAccessible(field)) {
                if (forceAccess) {
                    field.setAccessible(true);
                } else {
                    return null;
                }
            }
            return field;
        } catch (NoSuchFieldException e) {
        }
        return null;
    }

    /**
     * Read an accessible static Field.
     * 
     * @param field
     *            to read
     * @return the field value
     * @throws IllegalArgumentException
     *             if the field is null or not static
     * @throws IllegalAccessException
     *             if the field is not accessible
     */
    public static Object readStaticField(Field field)
            throws IllegalAccessException {
        return readStaticField(field, false);
    }

    /**
     * Read a static Field.
     * 
     * @param field
     *            to read
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method.
     * @return the field value
     * @throws IllegalArgumentException
     *             if the field is null or not static
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static Object readStaticField(Field field, boolean forceAccess)
            throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (!Modifier.isStatic(field.getModifiers())) {
            throw new IllegalArgumentException("The field '" + field.getName()
                    + "' is not static");
        }
        return readField(field, (Object) null, forceAccess);
    }

    /**
     * Read the named public static field. Superclasses will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the field is not accessible
     */
    public static Object readStaticField(Class<?> cls, String fieldName)
            throws IllegalAccessException {
        return readStaticField(cls, fieldName, false);
    }

    /**
     * Read the named static field. Superclasses will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static Object readStaticField(Class<?> cls, String fieldName,
            boolean forceAccess) throws IllegalAccessException {
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field "
                    + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        return readStaticField(field, false);
    }

    /**
     * Gets a static Field value by name. The field must be public. Only the
     * specified class will be considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the field is not accessible
     */
    public static Object readDeclaredStaticField(Class<?> cls, String fieldName)
            throws IllegalAccessException {
        return readDeclaredStaticField(cls, fieldName, false);
    }

    /**
     * Gets a static Field value by name. Only the specified class will be
     * considered.
     * 
     * @param cls
     *            the class to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static Object readDeclaredStaticField(Class<?> cls,
            String fieldName, boolean forceAccess)
            throws IllegalAccessException {
        Field field = getDeclaredField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field "
                    + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        return readStaticField(field, false);
    }

    /**
     * Read an accessible Field.
     * 
     * @param field
     *            the field to use
     * @param target
     *            the object to call on, may be null for static fields
     * @return the field value
     * @throws IllegalArgumentException
     *             if the field is null
     * @throws IllegalAccessException
     *             if the field is not accessible
     */
    public static Object readField(Field field, Object target)
            throws IllegalAccessException {
        return readField(field, target, false);
    }

    /**
     * Read a Field.
     * 
     * @param field
     *            the field to use
     * @param target
     *            the object to call on, may be null for static fields
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method.
     * @return the field value
     * @throws IllegalArgumentException
     *             if the field is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static Object readField(Field field, Object target,
            boolean forceAccess) throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            setAccessibleWorkaround(field);
        }
        return field.get(target);
    }

    /**
     * Read the named public field. Superclasses will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the named field is not public
     */
    public static Object readField(Object target, String fieldName)
            throws IllegalAccessException {
        return readField(target, fieldName, false);
    }

    /**
     * Read the named field. Superclasses will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the field value
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the named field is not made accessible
     */
    public static Object readField(Object target, String fieldName,
            boolean forceAccess) throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class<?> cls = target.getClass();
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field "
                    + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        return readField(field, target);
    }

    /**
     * Read the named public field. Only the class of the specified object will
     * be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @return the value of the field
     * @throws IllegalArgumentException
     *             if the class or field name is null
     * @throws IllegalAccessException
     *             if the named field is not public
     */
    public static Object readDeclaredField(Object target, String fieldName)
            throws IllegalAccessException {
        return readDeclaredField(target, fieldName, false);
    }

    /**
     * <p<>Gets a Field value by name. Only the class of the specified object
     * will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @return the Field object
     * @throws IllegalArgumentException
     *             if <code>target</code> or <code>fieldName</code> is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static Object readDeclaredField(Object target, String fieldName,
            boolean forceAccess) throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class<?> cls = target.getClass();
        Field field = getDeclaredField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field "
                    + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        return readField(field, target);
    }

    /**
     * Write a public static Field.
     * 
     * @param field
     *            to write
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if the field is null or not static
     * @throws IllegalAccessException
     *             if the field is not public or is final
     */
    public static void writeStaticField(Field field, Object value)
            throws IllegalAccessException {
        writeStaticField(field, value, false);
    }

    /**
     * Write a static Field.
     * 
     * @param field
     *            to write
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if the field is null or not static
     * @throws IllegalAccessException
     *             if the field is not made accessible or is final
     */
    public static void writeStaticField(Field field, Object value,
            boolean forceAccess) throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (!Modifier.isStatic(field.getModifiers())) {
            throw new IllegalArgumentException("The field '" + field.getName()
                    + "' is not static");
        }
        writeField(field, (Object) null, value, forceAccess);
    }

    /**
     * Write a named public static Field. Superclasses will be considered.
     * 
     * @param cls
     *            Class on which the Field is to be found
     * @param fieldName
     *            to write
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if the field cannot be located or is not static
     * @throws IllegalAccessException
     *             if the field is not public or is final
     */
    public static void writeStaticField(Class<?> cls, String fieldName,
            Object value) throws IllegalAccessException {
        writeStaticField(cls, fieldName, value, false);
    }

    /**
     * Write a named static Field. Superclasses will be considered.
     * 
     * @param cls
     *            Class on which the Field is to be found
     * @param fieldName
     *            to write
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if the field cannot be located or is not static
     * @throws IllegalAccessException
     *             if the field is not made accessible or is final
     */
    public static void writeStaticField(Class<?> cls, String fieldName,
            Object value, boolean forceAccess) throws IllegalAccessException {
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate field "
                    + fieldName + " on " + cls);
        }
        // already forced access above, don't repeat it here:
        writeStaticField(field, value);
    }

    /**
     * Write a named public static Field. Only the specified class will be
     * considered.
     * 
     * @param cls
     *            Class on which the Field is to be found
     * @param fieldName
     *            to write
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if the field cannot be located or is not static
     * @throws IllegalAccessException
     *             if the field is not public or is final
     */
    public static void writeDeclaredStaticField(Class<?> cls, String fieldName,
            Object value) throws IllegalAccessException {
        writeDeclaredStaticField(cls, fieldName, value, false);
    }

    /**
     * Write a named static Field. Only the specified class will be considered.
     * 
     * @param cls
     *            Class on which the Field is to be found
     * @param fieldName
     *            to write
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if the field cannot be located or is not static
     * @throws IllegalAccessException
     *             if the field is not made accessible or is final
     */
    public static void writeDeclaredStaticField(Class<?> cls, String fieldName,
            Object value, boolean forceAccess) throws IllegalAccessException {
        Field field = getDeclaredField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field "
                    + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        writeField(field, (Object) null, value);
    }

    /**
     * Write an accessible field.
     * 
     * @param field
     *            to write
     * @param target
     *            the object to call on, may be null for static fields
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if the field is null
     * @throws IllegalAccessException
     *             if the field is not accessible or is final
     */
    public static void writeField(Field field, Object target, Object value)
            throws IllegalAccessException {
        writeField(field, target, value, false);
    }

    /**
     * Write a field.
     * 
     * @param field
     *            to write
     * @param target
     *            the object to call on, may be null for static fields
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if the field is null
     * @throws IllegalAccessException
     *             if the field is not made accessible or is final
     */
    public static void writeField(Field field, Object target, Object value,
            boolean forceAccess) throws IllegalAccessException {
        if (field == null) {
            throw new IllegalArgumentException("The field must not be null");
        }
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            setAccessibleWorkaround(field);
        }
        field.set(target, value);
    }

    /**
     * Write a public field. Superclasses will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if <code>target</code> or <code>fieldName</code> is null
     * @throws IllegalAccessException
     *             if the field is not accessible
     */
    public static void writeField(Object target, String fieldName, Object value)
            throws IllegalAccessException {
        writeField(target, fieldName, value, false);
    }

    /**
     * Write a field. Superclasses will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if <code>target</code> or <code>fieldName</code> is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static void writeField(Object target, String fieldName,
            Object value, boolean forceAccess) throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class<?> cls = target.getClass();
        Field field = getField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field "
                    + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        writeField(field, target, value);
    }

    /**
     * Write a public field. Only the specified class will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param value
     *            to set
     * @throws IllegalArgumentException
     *             if <code>target</code> or <code>fieldName</code> is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static void writeDeclaredField(Object target, String fieldName,
            Object value) throws IllegalAccessException {
        writeDeclaredField(target, fieldName, value, false);
    }

    /**
     * Write a public field. Only the specified class will be considered.
     * 
     * @param target
     *            the object to reflect, must not be null
     * @param fieldName
     *            the field name to obtain
     * @param value
     *            to set
     * @param forceAccess
     *            whether to break scope restrictions using the
     *            <code>setAccessible</code> method. <code>False</code> will
     *            only match public fields.
     * @throws IllegalArgumentException
     *             if <code>target</code> or <code>fieldName</code> is null
     * @throws IllegalAccessException
     *             if the field is not made accessible
     */
    public static void writeDeclaredField(Object target, String fieldName,
            Object value, boolean forceAccess) throws IllegalAccessException {
        if (target == null) {
            throw new IllegalArgumentException("target object must not be null");
        }
        Class<?> cls = target.getClass();
        Field field = getDeclaredField(cls, fieldName, forceAccess);
        if (field == null) {
            throw new IllegalArgumentException("Cannot locate declared field "
                    + cls.getName() + "." + fieldName);
        }
        // already forced access above, don't repeat it here:
        writeField(field, target, value);
    }

    // ///////////////////////////////////////////
    //
    // TypeUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target type
     * following the Java generics rules. If both types are {@link Class}
     * objects, the method returns the result of
     * {@link ClassUtils#isAssignable(Class, Class)}.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toType
     *            the target type
     * @return <code>true</code> if <code>type</code> is assignable to
     *         <code>toType</code>.
     */
    public static boolean isAssignable(Type type, Type toType) {
        return isAssignable(type, toType, null);
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target type
     * following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toType
     *            the target type
     * @param typeVarAssigns
     *            optional map of type variable assignments
     * @return <code>true</code> if <code>type</code> is assignable to
     *         <code>toType</code>.
     */
    private static boolean isAssignable(Type type, Type toType,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (toType == null || toType instanceof Class<?>) {
            return isAssignable(type, (Class<?>) toType);
        }

        if (toType instanceof ParameterizedType) {
            return isAssignable(type, (ParameterizedType) toType,
                    typeVarAssigns);
        }

        if (toType instanceof GenericArrayType) {
            return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
        }

        if (toType instanceof WildcardType) {
            return isAssignable(type, (WildcardType) toType, typeVarAssigns);
        }

        // *
        if (toType instanceof TypeVariable<?>) {
            return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
        }
        // */

        throw new IllegalStateException("found an unhandled type: " + toType);
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target class
     * following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toClass
     *            the target class
     * @return true if <code>type</code> is assignable to <code>toClass</code>.
     */
    private static boolean isAssignable(Type type, Class<?> toClass) {
        if (type == null) {
            // consistency with ClassUtils.isAssignable() behavior
            return toClass == null || !toClass.isPrimitive();
        }

        // only a null type can be assigned to null type which
        // would have cause the previous to return true
        if (toClass == null) {
            return false;
        }

        // all types are assignable to themselves
        if (toClass.equals(type)) {
            return true;
        }

        if (type instanceof Class<?>) {
            // just comparing two classes
            return isAssignable((Class<?>) type, toClass);
        }

        if (type instanceof ParameterizedType) {
            // only have to compare the raw type to the class
            return isAssignable(getRawType((ParameterizedType) type), toClass);
        }

        // *
        if (type instanceof TypeVariable<?>) {
            // if any of the bounds are assignable to the class, then the
            // type is assignable to the class.
            for (Type bound : ((TypeVariable<?>) type).getBounds()) {
                if (isAssignable(bound, toClass)) {
                    return true;
                }
            }

            return false;
        }

        // the only classes to which a generic array type can be assigned
        // are class Object and array classes
        if (type instanceof GenericArrayType) {
            return toClass.equals(Object.class)
                    || toClass.isArray()
                    && isAssignable(
                            ((GenericArrayType) type).getGenericComponentType(),
                            toClass.getComponentType());
        }

        // wildcard types are not assignable to a class (though one would think
        // "? super Object" would be assignable to Object)
        if (type instanceof WildcardType) {
            return false;
        }

        throw new IllegalStateException("found an unhandled type: " + type);
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target
     * parameterized type following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toParameterizedType
     *            the target parameterized type
     * @return true if <code>type</code> is assignable to <code>toType</code>.
     */
    private static boolean isAssignable(Type type,
            ParameterizedType toParameterizedType,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (type == null) {
            return true;
        }

        // only a null type can be assigned to null type which
        // would have cause the previous to return true
        if (toParameterizedType == null) {
            return false;
        }

        // all types are assignable to themselves
        if (toParameterizedType.equals(type)) {
            return true;
        }

        // get the target type's raw type
        Class<?> toClass = getRawType(toParameterizedType);
        // get the subject type's type arguments including owner type arguments
        // and supertype arguments up to and including the target class.
        Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type,
                toClass, null);

        // null means the two types are not compatible
        if (fromTypeVarAssigns == null) {
            return false;
        }

        // compatible types, but there's no type arguments. this is equivalent
        // to comparing Map< ?, ? > to Map, and raw types are always assignable
        // to parameterized types.
        if (fromTypeVarAssigns.isEmpty()) {
            return true;
        }

        // get the target type's type arguments including owner type arguments
        Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(
                toParameterizedType, toClass, typeVarAssigns);

        // now to check each type argument
        for (Map.Entry<TypeVariable<?>, Type> entry : toTypeVarAssigns
                .entrySet()) {
            Type toTypeArg = entry.getValue();
            Type fromTypeArg = fromTypeVarAssigns.get(entry.getKey());

            // parameters must either be absent from the subject type, within
            // the bounds of the wildcard type, or be an exact match to the
            // parameters of the target type.
            if (fromTypeArg != null
                    && !toTypeArg.equals(fromTypeArg)
                    && !(toTypeArg instanceof WildcardType && isAssignable(
                            fromTypeArg, toTypeArg, typeVarAssigns))) {
                return false;
            }
        }

        return true;
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target generic
     * array type following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toGenericArrayType
     *            the target generic array type
     * @return true if <code>type</code> is assignable to
     *         <code>toGenericArrayType</code>.
     */
    private static boolean isAssignable(Type type,
            GenericArrayType toGenericArrayType,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (type == null) {
            return true;
        }

        // only a null type can be assigned to null type which
        // would have cause the previous to return true
        if (toGenericArrayType == null) {
            return false;
        }

        // all types are assignable to themselves
        if (toGenericArrayType.equals(type)) {
            return true;
        }

        Type toComponentType = toGenericArrayType.getGenericComponentType();

        if (type instanceof Class<?>) {
            Class<?> cls = (Class<?>) type;

            // compare the component types
            return cls.isArray()
                    && isAssignable(cls.getComponentType(), toComponentType,
                            typeVarAssigns);
        }

        if (type instanceof GenericArrayType) {
            // compare the component types
            return isAssignable(
                    ((GenericArrayType) type).getGenericComponentType(),
                    toComponentType, typeVarAssigns);
        }

        if (type instanceof WildcardType) {
            // so long as one of the upper bounds is assignable, it's good
            for (Type bound : getImplicitUpperBounds((WildcardType) type)) {
                if (isAssignable(bound, toGenericArrayType)) {
                    return true;
                }
            }

            return false;
        }

        if (type instanceof TypeVariable<?>) {
            // probably should remove the following logic and just return false.
            // type variables cannot specify arrays as bounds.
            for (Type bound : getImplicitBounds((TypeVariable<?>) type)) {
                if (isAssignable(bound, toGenericArrayType)) {
                    return true;
                }
            }

            return false;
        }

        if (type instanceof ParameterizedType) {
            // the raw type of a parameterized type is never an array or
            // generic array, otherwise the declaration would look like this:
            // Collection[]< ? extends String > collection;
            return false;
        }

        throw new IllegalStateException("found an unhandled type: " + type);
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target wildcard
     * type following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toWildcardType
     *            the target wildcard type
     * @return true if <code>type</code> is assignable to
     *         <code>toWildcardType</code>.
     */
    private static boolean isAssignable(Type type, WildcardType toWildcardType,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (type == null) {
            return true;
        }

        // only a null type can be assigned to null type which
        // would have cause the previous to return true
        if (toWildcardType == null) {
            return false;
        }

        // all types are assignable to themselves
        if (toWildcardType.equals(type)) {
            return true;
        }

        Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
        Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);

        if (type instanceof WildcardType) {
            WildcardType wildcardType = (WildcardType) type;
            Type[] upperBounds = getImplicitUpperBounds(wildcardType);
            Type[] lowerBounds = getImplicitLowerBounds(wildcardType);

            for (Type toBound : toUpperBounds) {
                // if there are assignments for unresolved type variables,
                // now's the time to substitute them.
                toBound = substituteTypeVariables(toBound, typeVarAssigns);

                // each upper bound of the subject type has to be assignable to
                // each
                // upper bound of the target type
                for (Type bound : upperBounds) {
                    if (!isAssignable(bound, toBound, typeVarAssigns)) {
                        return false;
                    }
                }
            }

            for (Type toBound : toLowerBounds) {
                // if there are assignments for unresolved type variables,
                // now's the time to substitute them.
                toBound = substituteTypeVariables(toBound, typeVarAssigns);

                // each lower bound of the target type has to be assignable to
                // each
                // lower bound of the subject type
                for (Type bound : lowerBounds) {
                    if (!isAssignable(toBound, bound, typeVarAssigns)) {
                        return false;
                    }
                }
            }

            return true;
        }

        for (Type toBound : toUpperBounds) {
            // if there are assignments for unresolved type variables,
            // now's the time to substitute them.
            if (!isAssignable(type,
                    substituteTypeVariables(toBound, typeVarAssigns),
                    typeVarAssigns)) {
                return false;
            }
        }

        for (Type toBound : toLowerBounds) {
            // if there are assignments for unresolved type variables,
            // now's the time to substitute them.
            if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns),
                    type, typeVarAssigns)) {
                return false;
            }
        }

        return true;
    }

    /**
     * <p>
     * Checks if the subject type may be implicitly cast to the target type
     * variable following the Java generics rules.
     * </p>
     * 
     * @param type
     *            the subject type to be assigned to the target type
     * @param toTypeVariable
     *            the target type variable
     * @return true if <code>type</code> is assignable to
     *         <code>toTypeVariable</code>.
     */
    private static boolean isAssignable(Type type,
            TypeVariable<?> toTypeVariable,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (type == null) {
            return true;
        }

        // only a null type can be assigned to null type which
        // would have cause the previous to return true
        if (toTypeVariable == null) {
            return false;
        }

        // all types are assignable to themselves
        if (toTypeVariable.equals(type)) {
            return true;
        }

        if (type instanceof TypeVariable<?>) {
            // a type variable is assignable to another type variable, if
            // and only if the former is the latter, extends the latter, or
            // is otherwise a descendant of the latter.
            Type[] bounds = getImplicitBounds((TypeVariable<?>) type);

            for (Type bound : bounds) {
                if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
                    return true;
                }
            }
        }

        if (type instanceof Class<?> || type instanceof ParameterizedType
                || type instanceof GenericArrayType
                || type instanceof WildcardType) {
            return false;
        }

        throw new IllegalStateException("found an unhandled type: " + type);
    }

    /**
     * <p>
     * </p>
     * 
     * @param type
     * @param typeVarAssigns
     * @return
     */
    private static Type substituteTypeVariables(Type type,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
            Type replacementType = typeVarAssigns.get(type);

            if (replacementType == null) {
                throw new IllegalArgumentException(
                        "missing assignment type for type variable " + type);
            }

            return replacementType;
        }

        return type;
    }

    /**
     * <p>
     * Retrieves all the type arguments for this parameterized type including
     * owner hierarchy arguments such as <code>
     * Outer<K,V>.Inner<T>.DeepInner<E></code> . The arguments are returned in a
     * {@link Map} specifying the argument type for each {@link TypeVariable}.
     * </p>
     * 
     * @param type
     *            specifies the subject parameterized type from which to harvest
     *            the parameters.
     * @return a map of the type arguments to their respective type variables.
     */
    public static Map<TypeVariable<?>, Type> getTypeArguments(
            ParameterizedType type) {
        return getTypeArguments(type, getRawType(type), null);
    }

    /**
     * <p>
     * Gets the type arguments of a class/interface based on a subtype. For
     * instance, this method will determine that both of the parameters for the
     * interface {@link Map} are {@link Object} for the subtype
     * {@link java.util.Properties Properties} even though the subtype does not
     * directly implement the <code>Map</code> interface.
     * <p>
     * </p>
     * This method returns <code>null</code> if <code>type</code> is not
     * assignable to <code>toClass</code>. It returns an empty map if none of
     * the classes or interfaces in its inheritance hierarchy specify any type
     * arguments. </p>
     * <p>
     * A side-effect of this method is that it also retrieves the type arguments
     * for the classes and interfaces that are part of the hierarchy between
     * <code>type</code> and <code>toClass</code>. So with the above example,
     * this method will also determine that the type arguments for
     * {@link java.util.Hashtable Hashtable} are also both <code>Object</code>.
     * In cases where the interface specified by <code>toClass</code> is
     * (indirectly) implemented more than once (e.g. where <code>toClass</code>
     * specifies the interface {@link java.lang.Iterable Iterable} and
     * <code>type</code> specifies a parameterized type that implements both
     * {@link java.util.Set Set} and {@link java.util.Collection Collection}),
     * this method will look at the inheritance hierarchy of only one of the
     * implementations/subclasses; the first interface encountered that isn't a
     * subinterface to one of the others in the <code>type</code> to
     * <code>toClass</code> hierarchy.
     * </p>
     * 
     * @param type
     *            the type from which to determine the type parameters of
     *            <code>toClass</code>
     * @param toClass
     *            the class whose type parameters are to be determined based on
     *            the subtype <code>type</code>
     * @return a map of the type assignments for the type variables in each type
     *         in the inheritance hierarchy from <code>type</code> to
     *         <code>toClass</code> inclusive.
     */
    public static Map<TypeVariable<?>, Type> getTypeArguments(Type type,
            Class<?> toClass) {
        return getTypeArguments(type, toClass, null);
    }

    /**
     * <p>
     * Return a map of the type arguments of <code>type</code> in the context of
     * <code>toClass</code>.
     * </p>
     * 
     * @param type
     * @param toClass
     * @param subtypeVarAssigns
     * @return
     */
    private static Map<TypeVariable<?>, Type> getTypeArguments(Type type,
            Class<?> toClass, Map<TypeVariable<?>, Type> subtypeVarAssigns) {
        if (type instanceof Class<?>) {
            return getTypeArguments((Class<?>) type, toClass, subtypeVarAssigns);
        }

        if (type instanceof ParameterizedType) {
            return getTypeArguments((ParameterizedType) type, toClass,
                    subtypeVarAssigns);
        }

        if (type instanceof GenericArrayType) {
            return getTypeArguments(
                    ((GenericArrayType) type).getGenericComponentType(),
                    toClass.isArray() ? toClass.getComponentType() : toClass,
                    subtypeVarAssigns);
        }

        // since wildcard types are not assignable to classes, should this just
        // return null?
        if (type instanceof WildcardType) {
            for (Type bound : getImplicitUpperBounds((WildcardType) type)) {
                // find the first bound that is assignable to the target class
                if (isAssignable(bound, toClass)) {
                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
                }
            }

            return null;
        }

        // *
        if (type instanceof TypeVariable<?>) {
            for (Type bound : getImplicitBounds((TypeVariable<?>) type)) {
                // find the first bound that is assignable to the target class
                if (isAssignable(bound, toClass)) {
                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
                }
            }

            return null;
        }
        // */

        throw new IllegalStateException("found an unhandled type: " + type);
    }

    /**
     * <p>
     * </p>
     * 
     * @param parameterizedType
     * @param toClass
     * @param subtypeVarAssigns
     * @return
     */
    private static Map<TypeVariable<?>, Type> getTypeArguments(
            ParameterizedType parameterizedType, Class<?> toClass,
            Map<TypeVariable<?>, Type> subtypeVarAssigns) {
        Class<?> cls = getRawType(parameterizedType);

        // make sure they're assignable
        if (!isAssignable(cls, toClass)) {
            return null;
        }

        Type ownerType = parameterizedType.getOwnerType();
        Map<TypeVariable<?>, Type> typeVarAssigns;

        if (ownerType instanceof ParameterizedType) {
            // get the owner type arguments first
            ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
            typeVarAssigns = getTypeArguments(parameterizedOwnerType,
                    getRawType(parameterizedOwnerType), subtypeVarAssigns);
        } else {
            // no owner, prep the type variable assignments map
            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>()
                    : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns);
        }

        // get the subject parameterized type's arguments
        Type[] typeArgs = parameterizedType.getActualTypeArguments();
        // and get the corresponding type variables from the raw class
        TypeVariable<?>[] typeParams = cls.getTypeParameters();

        // map the arguments to their respective type variables
        for (int i = 0; i < typeParams.length; i++) {
            Type typeArg = typeArgs[i];
            typeVarAssigns.put(typeParams[i], typeVarAssigns
                    .containsKey(typeArg) ? typeVarAssigns.get(typeArg)
                    : typeArg);
        }

        if (toClass.equals(cls)) {
            // target class has been reached. Done.
            return typeVarAssigns;
        }

        // walk the inheritance hierarchy until the target class is reached
        return getTypeArguments(getClosestParentType(cls, toClass), toClass,
                typeVarAssigns);
    }

    /**
     * <p>
     * </p>
     * 
     * @param cls
     * @param toClass
     * @param subtypeVarAssigns
     * @return
     */
    private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls,
            Class<?> toClass, Map<TypeVariable<?>, Type> subtypeVarAssigns) {
        // make sure they're assignable
        if (!isAssignable(cls, toClass)) {
            return null;
        }

        // can't work with primitives
        if (cls.isPrimitive()) {
            // both classes are primitives?
            if (toClass.isPrimitive()) {
                // dealing with widening here. No type arguments to be
                // harvested with these two types.
                return new HashMap<TypeVariable<?>, Type>();
            }

            // work with wrapper the wrapper class instead of the primitive
            cls = primitiveToWrapper(cls);
        }

        // create a copy of the incoming map, or an empty one if it's null
        HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<TypeVariable<?>, Type>()
                : new HashMap<TypeVariable<?>, Type>(subtypeVarAssigns);

        // no arguments for the parameters, or target class has been reached
        if (cls.getTypeParameters().length > 0 || toClass.equals(cls)) {
            return typeVarAssigns;
        }

        // walk the inheritance hierarchy until the target class is reached
        return getTypeArguments(getClosestParentType(cls, toClass), toClass,
                typeVarAssigns);
    }

    /**
     * <p>
     * Tries to determine the type arguments of a class/interface based on a
     * super parameterized type's type arguments. This method is the inverse of
     * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
     * type arguments based on a subtype. It is far more limited in determining
     * the type arguments for the subject class's type variables in that it can
     * only determine those parameters that map from the subject {@link Class}
     * object to the supertype.
     * </p>
     * <p>
     * Example: {@link java.util.TreeSet TreeSet} sets its parameter as the
     * parameter for {@link java.util.NavigableSet NavigableSet}, which in turn
     * sets the parameter of {@link java.util.SortedSet}, which in turn sets the
     * parameter of {@link Set}, which in turn sets the parameter of
     * {@link java.util.Collection}, which in turn sets the parameter of
     * {@link java.lang.Iterable}. Since <code>TreeSet</code>'s parameter maps
     * (indirectly) to <code>Iterable</code>'s parameter, it will be able to
     * determine that based on the super type <code>Iterable<? extends
     * Map<Integer,? extends Collection<?>>></code>, the parameter of
     * <code>TreeSet</code> is <code>? extends Map<Integer,? extends
     * Collection<?>></code>.
     * </p>
     * 
     * @param cls
     *            the class whose type parameters are to be determined
     * @param superType
     *            the super type from which <code>cls</code>'s type arguments
     *            are to be determined
     * @return a map of the type assignments that could be determined for the
     *         type variables in each type in the inheritance hierarchy from
     *         <code>type</code> to <code>toClass</code> inclusive.
     */
    public static Map<TypeVariable<?>, Type> determineTypeArguments(
            Class<?> cls, ParameterizedType superType) {
        Class<?> superClass = getRawType(superType);

        // compatibility check
        if (!isAssignable(cls, superClass)) {
            return null;
        }

        if (cls.equals(superClass)) {
            return getTypeArguments(superType, superClass, null);
        }

        // get the next class in the inheritance hierarchy
        Type midType = getClosestParentType(cls, superClass);

        // can only be a class or a parameterized type
        if (midType instanceof Class<?>) {
            return determineTypeArguments((Class<?>) midType, superType);
        }

        ParameterizedType midParameterizedType = (ParameterizedType) midType;
        Class<?> midClass = getRawType(midParameterizedType);
        // get the type variables of the mid class that map to the type
        // arguments of the super class
        Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(
                midClass, superType);
        // map the arguments of the mid type to the class type variables
        mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);

        return typeVarAssigns;
    }

    /**
     * <p>
     * </p>
     * 
     * @param cls
     * @param parameterizedType
     * @param typeVarAssigns
     */
    private static <T> void mapTypeVariablesToArguments(Class<T> cls,
            ParameterizedType parameterizedType,
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        // capture the type variables from the owner type that have assignments
        Type ownerType = parameterizedType.getOwnerType();

        if (ownerType instanceof ParameterizedType) {
            // recursion to make sure the owner's owner type gets processed
            mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType,
                    typeVarAssigns);
        }

        // parameterizedType is a generic interface/class (or it's in the owner
        // hierarchy of said interface/class) implemented/extended by the class
        // cls. Find out which type variables of cls are type arguments of
        // parameterizedType:
        Type[] typeArgs = parameterizedType.getActualTypeArguments();

        // of the cls's type variables that are arguments of parameterizedType,
        // find out which ones can be determined from the super type's arguments
        TypeVariable<?>[] typeVars = getRawType(parameterizedType)
                .getTypeParameters();

        // use List view of type parameters of cls so the contains() method can
        // be used:
        List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls
                .getTypeParameters());

        for (int i = 0; i < typeArgs.length; i++) {
            TypeVariable<?> typeVar = typeVars[i];
            Type typeArg = typeArgs[i];

            // argument of parameterizedType is a type variable of cls
            if (typeVarList.contains(typeArg)
            // type variable of parameterizedType has an assignment in
            // the super type.
                    && typeVarAssigns.containsKey(typeVar)) {
                // map the assignment to the cls's type variable
                typeVarAssigns.put((TypeVariable<?>) typeArg,
                        typeVarAssigns.get(typeVar));
            }
        }
    }

    /**
     * <p>
     * Closest parent type? Closest to what? The closest parent type to the
     * super class specified by <code>superClass</code>.
     * </p>
     * 
     * @param cls
     * @param superClass
     * @return
     */
    private static Type getClosestParentType(Class<?> cls, Class<?> superClass) {
        // only look at the interfaces if the super class is also an interface
        if (superClass.isInterface()) {
            // get the generic interfaces of the subject class
            Type[] interfaceTypes = cls.getGenericInterfaces();
            // will hold the best generic interface match found
            Type genericInterface = null;

            // find the interface closest to the super class
            for (int i = 0; i < interfaceTypes.length; i++) {
                Type midType = interfaceTypes[i];
                Class<?> midClass = null;

                if (midType instanceof ParameterizedType) {
                    midClass = getRawType((ParameterizedType) midType);
                } else if (midType instanceof Class<?>) {
                    midClass = (Class<?>) midType;
                } else {
                    throw new IllegalStateException("Unexpected generic"
                            + " interface type found: " + midType);
                }

                // check if this interface is further up the inheritance chain
                // than the previously found match
                if (isAssignable(midClass, superClass)
                        && isAssignable(genericInterface, (Type) midClass)) {
                    genericInterface = midType;
                }
            }

            // found a match?
            if (genericInterface != null) {
                return genericInterface;
            }
        }

        // none of the interfaces were descendants of the target class, so the
        // super class has to be one, instead
        return cls.getGenericSuperclass();
    }

    /**
     * <p>
     * Checks if the given value can be assigned to the target type following
     * the Java generics rules.
     * </p>
     * 
     * @param value
     * @param type
     * @return true of <code>value</code> is an instance of <code>type</code>.
     */
    public static boolean isInstance(Object value, Type type) {
        if (type == null) {
            return false;
        }

        return value == null ? !(type instanceof Class<?>)
                || !((Class<?>) type).isPrimitive() : isAssignable(
                value.getClass(), type, null);
    }

    /**
     * <p>
     * This method strips out the redundant upper bound types in type variable
     * types and wildcard types (or it would with wildcard types if multiple
     * upper bounds were allowed).
     * </p>
     * <p>
     * Example: with the variable type declaration:
     * 
     * <pre>
     * &lt;K extends java.util.Collection&lt;String&gt; &amp;
     * java.util.List&lt;String&gt;&gt;
     * </pre>
     * 
     * since <code>List</code> is a subinterface of <code>Collection</code>,
     * this method will return the bounds as if the declaration had been:
     * 
     * <pre>
     * &lt;K extends java.util.List&lt;String&gt;&gt;
     * </pre>
     * 
     * </p>
     * 
     * @param bounds
     *            an array of types representing the upper bounds of either
     *            <code>WildcardType</code> or <code>TypeVariable</code>.
     * @return an array containing the values from <code>bounds</code> minus the
     *         redundant types.
     */
    public static Type[] normalizeUpperBounds(Type[] bounds) {
        // don't bother if there's only one (or none) type
        if (bounds.length < 2) {
            return bounds;
        }

        Set<Type> types = new HashSet<Type>(bounds.length);

        for (Type type1 : bounds) {
            boolean subtypeFound = false;

            for (Type type2 : bounds) {
                if (type1 != type2 && isAssignable(type2, type1, null)) {
                    subtypeFound = true;
                    break;
                }
            }

            if (!subtypeFound) {
                types.add(type1);
            }
        }

        return types.toArray(new Type[0]);
    }

    /**
     * <p>
     * Returns an array containing the sole type of {@link Object} if
     * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
     * returns the result of <code>TypeVariable.getBounds()</code> passed into
     * {@link #normalizeUpperBounds}.
     * </p>
     * 
     * @param typeVariable
     *            the subject type variable
     * @return a non-empty array containing the bounds of the type variable.
     */
    public static Type[] getImplicitBounds(TypeVariable<?> typeVariable) {
        Type[] bounds = typeVariable.getBounds();

        return bounds.length == 0 ? new Type[] { Object.class }
                : normalizeUpperBounds(bounds);
    }

    /**
     * <p>
     * Returns an array containing the sole value of {@link Object} if
     * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
     * it returns the result of <code>WildcardType.getUpperBounds()</code>
     * passed into {@link #normalizeUpperBounds}.
     * </p>
     * 
     * @param wildcardType
     *            the subject wildcard type
     * @return a non-empty array containing the upper bounds of the wildcard
     *         type.
     */
    public static Type[] getImplicitUpperBounds(WildcardType wildcardType) {
        Type[] bounds = wildcardType.getUpperBounds();

        return bounds.length == 0 ? new Type[] { Object.class }
                : normalizeUpperBounds(bounds);
    }

    /**
     * <p>
     * Returns an array containing a single value of <code>null</code> if
     * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
     * it returns the result of <code>WildcardType.getLowerBounds()</code>.
     * </p>
     * 
     * @param wildcardType
     *            the subject wildcard type
     * @return a non-empty array containing the lower bounds of the wildcard
     *         type.
     */
    public static Type[] getImplicitLowerBounds(WildcardType wildcardType) {
        Type[] bounds = wildcardType.getLowerBounds();

        return bounds.length == 0 ? new Type[] { null } : bounds;
    }

    /**
     * <p>
     * Determines whether or not specified types satisfy the bounds of their
     * mapped type variables. When a type parameter extends another (such as
     * <code><T, S extends T></code>), uses another as a type parameter (such as
     * <code><T, S extends Comparable<T></code>), or otherwise depends on
     * another type variable to be specified, the dependencies must be included
     * in <code>typeVarAssigns</code>.
     * </p>
     * 
     * @param typeVarAssigns
     *            specifies the potential types to be assigned to the type
     *            variables.
     * @return whether or not the types can be assigned to their respective type
     *         variables.
     */
    public static boolean typesSatisfyVariables(
            Map<TypeVariable<?>, Type> typeVarAssigns) {
        // all types must be assignable to all the bounds of the their mapped
        // type variable.
        for (Map.Entry<TypeVariable<?>, Type> entry : typeVarAssigns.entrySet()) {
            TypeVariable<?> typeVar = entry.getKey();
            Type type = entry.getValue();

            for (Type bound : getImplicitBounds(typeVar)) {
                if (!isAssignable(type,
                        substituteTypeVariables(bound, typeVarAssigns),
                        typeVarAssigns)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * <p>
     * Type-checking method of convenience.
     * </p>
     * 
     * @param parameterizedType
     * @return
     */
    private static Class<?> getRawType(ParameterizedType parameterizedType) {
        Type rawType = parameterizedType.getRawType();

        // check if raw type is a Class object
        // not currently necessary, but since the return type is Type instead of
        // Class, there's enough reason to believe that future versions of Java
        // may return other Type implementations. And type-safety checking is
        // rarely a bad idea.
        if (!(rawType instanceof Class<?>)) {
            throw new IllegalStateException("Wait... What!? Type of rawType: "
                    + rawType);
        }

        return (Class<?>) rawType;
    }

    /**
     * <p>
     * Get the raw type of a Java type, given its context. Primarily for use
     * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
     * not know the runtime type of <code>type</code>: if you know you have a
     * {@link Class} instance, it is already raw; if you know you have a
     * {@link ParameterizedType}, its raw type is only a method call away.
     * </p>
     * 
     * @param type
     *            to resolve
     * @param assigningType
     *            type to be resolved against
     * @return the resolved <code>Class</code> object or <code>null</code> if
     *         the type could not be resolved
     */
    public static Class<?> getRawType(Type type, Type assigningType) {
        if (type instanceof Class<?>) {
            // it is raw, no problem
            return (Class<?>) type;
        }

        if (type instanceof ParameterizedType) {
            // simple enough to get the raw type of a ParameterizedType
            return getRawType((ParameterizedType) type);
        }

        if (type instanceof TypeVariable<?>) {
            if (assigningType == null) {
                return null;
            }

            // get the entity declaring this type variable
            Object genericDeclaration = ((TypeVariable<?>) type)
                    .getGenericDeclaration();

            // can't get the raw type of a method- or constructor-declared type
            // variable
            if (!(genericDeclaration instanceof Class<?>)) {
                return null;
            }

            // get the type arguments for the declaring class/interface based
            // on the enclosing type
            Map<TypeVariable<?>, Type> typeVarAssigns = getTypeArguments(
                    assigningType, (Class<?>) genericDeclaration);

            // enclosingType has to be a subclass (or subinterface) of the
            // declaring type
            if (typeVarAssigns == null) {
                return null;
            }

            // get the argument assigned to this type variable
            Type typeArgument = typeVarAssigns.get(type);

            if (typeArgument == null) {
                return null;
            }

            // get the argument for this type variable
            return getRawType(typeArgument, assigningType);
        }

        if (type instanceof GenericArrayType) {
            // get raw component type
            Class<?> rawComponentType = getRawType(
                    ((GenericArrayType) type).getGenericComponentType(),
                    assigningType);

            // create array type from raw component type and return its class
            return Array.newInstance(rawComponentType, 0).getClass();
        }

        // (hand-waving) this is not the method you're looking for
        if (type instanceof WildcardType) {
            return null;
        }

        throw new IllegalArgumentException("unknown type: " + type);
    }

    // ///////////////////////////////////////////
    //
    // DateUtils
    //
    // ///////////////////////////////////////////

    /**
     * The UTC time zone (often referred to as GMT).
     */
    public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("GMT");
    /**
     * Number of milliseconds in a standard second.
     * 
     * @since 2.1
     */
    public static final long MILLIS_PER_SECOND = 1000;
    /**
     * Number of milliseconds in a standard minute.
     * 
     * @since 2.1
     */
    public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
    /**
     * Number of milliseconds in a standard hour.
     * 
     * @since 2.1
     */
    public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
    /**
     * Number of milliseconds in a standard day.
     * 
     * @since 2.1
     */
    public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

    /**
     * This is half a month, so this represents whether a date is in the top or
     * bottom half of the month.
     */
    public final static int SEMI_MONTH = 1001;

    private static final int[][] fields = { { Calendar.MILLISECOND },
            { Calendar.SECOND }, { Calendar.MINUTE },
            { Calendar.HOUR_OF_DAY, Calendar.HOUR },
            { Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
            /*
             * Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK,
             * Calendar.DAY_OF_WEEK_IN_MONTH
             */
            }, { Calendar.MONTH, SEMI_MONTH }, { Calendar.YEAR },
            { Calendar.ERA } };

    /**
     * A week range, starting on Sunday.
     */
    public final static int RANGE_WEEK_SUNDAY = 1;

    /**
     * A week range, starting on Monday.
     */
    public final static int RANGE_WEEK_MONDAY = 2;

    /**
     * A week range, starting on the day focused.
     */
    public final static int RANGE_WEEK_RELATIVE = 3;

    /**
     * A week range, centered around the day focused.
     */
    public final static int RANGE_WEEK_CENTER = 4;

    /**
     * A month range, the week starting on Sunday.
     */
    public final static int RANGE_MONTH_SUNDAY = 5;

    /**
     * A month range, the week starting on Monday.
     */
    public final static int RANGE_MONTH_MONDAY = 6;

    /**
     * Constant marker for truncating
     * 
     * @since 3.0
     */
    public final static int MODIFY_TRUNCATE = 0;

    /**
     * Constant marker for rounding
     * 
     * @since 3.0
     */
    public final static int MODIFY_ROUND = 1;

    /**
     * Constant marker for ceiling
     * 
     * @since 3.0
     */
    public final static int MODIFY_CEILING = 2;

    /**
     * <p>
     * Checks if two date objects are on the same day ignoring time.
     * </p>
     * 
     * <p>
     * 28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. 28 Mar 2002
     * 13:45 and 12 Mar 2002 13:45 would return false.
     * </p>
     * 
     * @param date1
     *            the first date, not altered, not null
     * @param date2
     *            the second date, not altered, not null
     * @return true if they represent the same day
     * @throws IllegalArgumentException
     *             if either date is <code>null</code>
     * @since 2.1
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return isSameDay(cal1, cal2);
    }

    /**
     * <p>
     * Checks if two calendar objects are on the same day ignoring time.
     * </p>
     * 
     * <p>
     * 28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. 28 Mar 2002
     * 13:45 and 12 Mar 2002 13:45 would return false.
     * </p>
     * 
     * @param cal1
     *            the first calendar, not altered, not null
     * @param cal2
     *            the second calendar, not altered, not null
     * @return true if they represent the same day
     * @throws IllegalArgumentException
     *             if either calendar is <code>null</code>
     * @since 2.1
     */
    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA)
                && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1
                    .get(Calendar.DAY_OF_YEAR) == cal2
                .get(Calendar.DAY_OF_YEAR));
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if two date objects represent the same instant in time.
     * </p>
     * 
     * <p>
     * This method compares the long millisecond time of the two objects.
     * </p>
     * 
     * @param date1
     *            the first date, not altered, not null
     * @param date2
     *            the second date, not altered, not null
     * @return true if they represent the same millisecond instant
     * @throws IllegalArgumentException
     *             if either date is <code>null</code>
     * @since 2.1
     */
    public static boolean isSameInstant(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        return date1.getTime() == date2.getTime();
    }

    /**
     * <p>
     * Checks if two calendar objects represent the same instant in time.
     * </p>
     * 
     * <p>
     * This method compares the long millisecond time of the two objects.
     * </p>
     * 
     * @param cal1
     *            the first calendar, not altered, not null
     * @param cal2
     *            the second calendar, not altered, not null
     * @return true if they represent the same millisecond instant
     * @throws IllegalArgumentException
     *             if either date is <code>null</code>
     * @since 2.1
     */
    public static boolean isSameInstant(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        return cal1.getTime().getTime() == cal2.getTime().getTime();
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Checks if two calendar objects represent the same local time.
     * </p>
     * 
     * <p>
     * This method compares the values of the fields of the two objects. In
     * addition, both calendars must be the same of the same type.
     * </p>
     * 
     * @param cal1
     *            the first calendar, not altered, not null
     * @param cal2
     *            the second calendar, not altered, not null
     * @return true if they represent the same millisecond instant
     * @throws IllegalArgumentException
     *             if either date is <code>null</code>
     * @since 2.1
     */
    public static boolean isSameLocalTime(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        return (cal1.get(Calendar.MILLISECOND) == cal2
                .get(Calendar.MILLISECOND)
                && cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)
                && cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE)
                && cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)
                && cal1.get(Calendar.DAY_OF_YEAR) == cal2
                        .get(Calendar.DAY_OF_YEAR)
                && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
                && cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1
                    .getClass() == cal2.getClass());
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Parses a string representing a date by trying a variety of different
     * parsers.
     * </p>
     * 
     * <p>
     * The parse will try each parse pattern in turn. A parse is only deemed
     * successful if it parses the whole of the input string. If no parse
     * patterns match, a ParseException is thrown.
     * </p>
     * The parser will be lenient toward the parsed date.
     * 
     * @param str
     *            the date to parse, not null
     * @param parsePatterns
     *            the date format patterns to use, see SimpleDateFormat, not
     *            null
     * @return the parsed date
     * @throws IllegalArgumentException
     *             if the date string or pattern array is null
     * @throws ParseException
     *             if none of the date patterns were suitable (or there were
     *             none)
     */
    public static Date parseDate(String str, String... parsePatterns)
            throws ParseException {
        return parseDateWithLeniency(str, parsePatterns, true);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Parses a string representing a date by trying a variety of different
     * parsers.
     * </p>
     * 
     * <p>
     * The parse will try each parse pattern in turn. A parse is only deemed
     * successful if it parses the whole of the input string. If no parse
     * patterns match, a ParseException is thrown.
     * </p>
     * The parser parses strictly - it does not allow for dates such as
     * "February 942, 1996".
     * 
     * @param str
     *            the date to parse, not null
     * @param parsePatterns
     *            the date format patterns to use, see SimpleDateFormat, not
     *            null
     * @return the parsed date
     * @throws IllegalArgumentException
     *             if the date string or pattern array is null
     * @throws ParseException
     *             if none of the date patterns were suitable
     * @since 2.5
     */
    public static Date parseDateStrictly(String str, String... parsePatterns)
            throws ParseException {
        return parseDateWithLeniency(str, parsePatterns, false);
    }

    /**
     * <p>
     * Parses a string representing a date by trying a variety of different
     * parsers.
     * </p>
     * 
     * <p>
     * The parse will try each parse pattern in turn. A parse is only deemed
     * successful if it parses the whole of the input string. If no parse
     * patterns match, a ParseException is thrown.
     * </p>
     * 
     * @param str
     *            the date to parse, not null
     * @param parsePatterns
     *            the date format patterns to use, see SimpleDateFormat, not
     *            null
     * @param lenient
     *            Specify whether or not date/time parsing is to be lenient.
     * @return the parsed date
     * @throws IllegalArgumentException
     *             if the date string or pattern array is null
     * @throws ParseException
     *             if none of the date patterns were suitable
     * @see java.util.Calender#isLenient()
     */
    private static Date parseDateWithLeniency(String str,
            String[] parsePatterns, boolean lenient) throws ParseException {
        if (str == null || parsePatterns == null) {
            throw new IllegalArgumentException(
                    "Date and Patterns must not be null");
        }

        SimpleDateFormat parser = new SimpleDateFormat();
        parser.setLenient(lenient);
        ParsePosition pos = new ParsePosition(0);
        for (int i = 0; i < parsePatterns.length; i++) {

            String pattern = parsePatterns[i];

            // LANG-530 - need to make sure 'ZZ' output doesn't get passed to
            // SimpleDateFormat
            if (parsePatterns[i].endsWith("ZZ")) {
                pattern = pattern.substring(0, pattern.length() - 1);
            }

            parser.applyPattern(pattern);
            pos.setIndex(0);

            String str2 = str;
            // LANG-530 - need to make sure 'ZZ' output doesn't hit
            // SimpleDateFormat as it will ParseException
            if (parsePatterns[i].endsWith("ZZ")) {
                str2 = str.replaceAll("([-+][0-9][0-9]):([0-9][0-9])$", "$1$2");
            }

            Date date = parser.parse(str2, pos);
            if (date != null && pos.getIndex() == str2.length()) {
                return date;
            }
        }
        throw new ParseException("Unable to parse the date: " + str, -1);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of years to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addYears(Date date, int amount) {
        return addDate(date, Calendar.YEAR, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of months to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addMonths(Date date, int amount) {
        return addDate(date, Calendar.MONTH, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of weeks to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addWeeks(Date date, int amount) {
        return addDate(date, Calendar.WEEK_OF_YEAR, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of days to a date returning a new object. The original date
     * object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addDays(Date date, int amount) {
        return addDate(date, Calendar.DAY_OF_MONTH, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of hours to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addHours(Date date, int amount) {
        return addDate(date, Calendar.HOUR_OF_DAY, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of minutes to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addMinutes(Date date, int amount) {
        return addDate(date, Calendar.MINUTE, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of seconds to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addSeconds(Date date, int amount) {
        return addDate(date, Calendar.SECOND, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds a number of milliseconds to a date returning a new object. The
     * original date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    public static Date addMilliseconds(Date date, int amount) {
        return addDate(date, Calendar.MILLISECOND, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Adds to a date returning a new object. The original date object is
     * unchanged.
     * 
     * @param date
     *            the date, not null
     * @param calendarField
     *            the calendar field to add to
     * @param amount
     *            the amount to add, may be negative
     * @return the new date object with the amount added
     * @throws IllegalArgumentException
     *             if the date is null
     */
    private static Date addDate(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the years field to a date returning a new object. The original date
     * object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setYears(Date date, int amount) {
        return setDate(date, Calendar.YEAR, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the months field to a date returning a new object. The original date
     * object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setMonths(Date date, int amount) {
        return setDate(date, Calendar.MONTH, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the day of month field to a date returning a new object. The
     * original date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setDays(Date date, int amount) {
        return setDate(date, Calendar.DAY_OF_MONTH, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the hours field to a date returning a new object. Hours range from
     * 0-23. The original date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setHours(Date date, int amount) {
        return setDate(date, Calendar.HOUR_OF_DAY, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the minute field to a date returning a new object. The original date
     * object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setMinutes(Date date, int amount) {
        return setDate(date, Calendar.MINUTE, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the seconds field to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setSeconds(Date date, int amount) {
        return setDate(date, Calendar.SECOND, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the miliseconds field to a date returning a new object. The original
     * date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    public static Date setMilliseconds(Date date, int amount) {
        return setDate(date, Calendar.MILLISECOND, amount);
    }

    // -----------------------------------------------------------------------
    /**
     * Sets the specified field to a date returning a new object. This does not
     * use a lenient calendar. The original date object is unchanged.
     * 
     * @param date
     *            the date, not null
     * @param calendarField
     *            the calendar field to set the amount to
     * @param amount
     *            the amount to set
     * @return a new Date object set with the specified value
     * @throws IllegalArgumentException
     *             if the date is null
     * @since 2.4
     */
    private static Date setDate(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        // getInstance() returns a new object, so this method is thread safe.
        Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.set(calendarField, amount);
        return c.getTime();
    }

    // -----------------------------------------------------------------------
    /**
     * Convert a Date into a Calendar object.
     * 
     * @param date
     *            the date to convert to a Calendar
     * @return the created Calendar
     * @throws NullPointerException
     *             if null is passed in
     */
    public static Calendar toCalendar(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Round this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if this
     * was passed with HOUR, it would return 28 Mar 2002 14:00:00.000. If this
     * was passed with MONTH, it would return 1 April 2002 0:00:00.000.
     * </p>
     * 
     * <p>
     * For a date in a timezone that handles the change to daylight saving time,
     * rounding to Calendar.HOUR_OF_DAY will behave as follows. Suppose daylight
     * saving time begins at 02:00 on March 30. Rounding a date that crosses
     * this time would produce the following values:
     * <ul>
     * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
     * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Date roundDate(Date date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar gval = Calendar.getInstance();
        gval.setTime(date);
        modifyDate(gval, field, MODIFY_ROUND);
        return gval.getTime();
    }

    /**
     * <p>
     * Round this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if this
     * was passed with HOUR, it would return 28 Mar 2002 14:00:00.000. If this
     * was passed with MONTH, it would return 1 April 2002 0:00:00.000.
     * </p>
     * 
     * <p>
     * For a date in a timezone that handles the change to daylight saving time,
     * rounding to Calendar.HOUR_OF_DAY will behave as follows. Suppose daylight
     * saving time begins at 02:00 on March 30. Rounding a date that crosses
     * this time would produce the following values:
     * <ul>
     * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
     * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date (a different object)
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Calendar roundCalendar(Calendar date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar rounded = (Calendar) date.clone();
        modifyDate(rounded, field, MODIFY_ROUND);
        return rounded;
    }

    /**
     * <p>
     * Round this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if this
     * was passed with HOUR, it would return 28 Mar 2002 14:00:00.000. If this
     * was passed with MONTH, it would return 1 April 2002 0:00:00.000.
     * </p>
     * 
     * <p>
     * For a date in a timezone that handles the change to daylight saving time,
     * rounding to Calendar.HOUR_OF_DAY will behave as follows. Suppose daylight
     * saving time begins at 02:00 on March 30. Rounding a date that crosses
     * this time would produce the following values:
     * <ul>
     * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
     * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
     * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, either Date or Calendar
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ClassCastException
     *             if the object type is not a <code>Date</code> or
     *             <code>Calendar</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Date roundDateOrCalender(Object date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        if (date instanceof Date) {
            return roundDate((Date) date, field);
        } else if (date instanceof Calendar) {
            return roundCalendar((Calendar) date, field).getTime();
        } else {
            throw new ClassCastException("Could not round " + date);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Truncate this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Date truncateDate(Date date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar gval = Calendar.getInstance();
        gval.setTime(date);
        modifyDate(gval, field, MODIFY_TRUNCATE);
        return gval.getTime();
    }

    /**
     * <p>
     * Truncate this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date (a different object)
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Calendar truncateCalendar(Calendar date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar truncated = (Calendar) date.clone();
        modifyDate(truncated, field, MODIFY_TRUNCATE);
        return truncated;
    }

    /**
     * <p>
     * Truncate this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with, either <code>Date</code> or
     *            <code>Calendar</code>
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ClassCastException
     *             if the object type is not a <code>Date</code> or
     *             <code>Calendar</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    public static Date truncateDateOrCalender(Object date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        if (date instanceof Date) {
            return truncateDate((Date) date, field);
        } else if (date instanceof Calendar) {
            return truncateCalendar((Calendar) date, field).getTime();
        } else {
            throw new ClassCastException("Could not truncate " + date);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Ceil this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     * @since 2.5
     */
    public static Date ceilingDate(Date date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar gval = Calendar.getInstance();
        gval.setTime(date);
        modifyDate(gval, field, MODIFY_CEILING);
        return gval.getTime();
    }

    /**
     * <p>
     * Ceil this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date (a different object)
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     * @since 2.5
     */
    public static Calendar ceilingCalendar(Calendar date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar ceiled = (Calendar) date.clone();
        modifyDate(ceiled, field, MODIFY_CEILING);
        return ceiled;
    }

    /**
     * <p>
     * Ceil this date, leaving the field specified as the most significant
     * field.
     * </p>
     * 
     * <p>
     * For example, if you had the datetime of 28 Mar 2002 13:45:01.231, if you
     * passed with HOUR, it would return 28 Mar 2002 13:00:00.000. If this was
     * passed with MONTH, it would return 1 Mar 2002 0:00:00.000.
     * </p>
     * 
     * @param date
     *            the date to work with, either <code>Date</code> or
     *            <code>Calendar</code>
     * @param field
     *            the field from <code>Calendar</code> or
     *            <code>SEMI_MONTH</code>
     * @return the rounded date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ClassCastException
     *             if the object type is not a <code>Date</code> or
     *             <code>Calendar</code>
     * @throws ArithmeticException
     *             if the year is over 280 million
     * @since 2.5
     */
    public static Date ceilingDateOrCalender(Object date, int field) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        if (date instanceof Date) {
            return ceilingDate((Date) date, field);
        } else if (date instanceof Calendar) {
            return ceilingCalendar((Calendar) date, field).getTime();
        } else {
            throw new ClassCastException("Could not find ceiling of for type: "
                    + date.getClass());
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Internal calculation method.
     * </p>
     * 
     * @param val
     *            the calendar
     * @param field
     *            the field constant
     * @param modType
     *            type to truncate, round or ceiling
     * @throws ArithmeticException
     *             if the year is over 280 million
     */
    private static void modifyDate(Calendar val, int field, int modType) {
        if (val.get(Calendar.YEAR) > 280000000) {
            throw new ArithmeticException(
                    "Calendar value too large for accurate calculations");
        }

        if (field == Calendar.MILLISECOND) {
            return;
        }

        // ----------------- Fix for LANG-59 ---------------------- START
        // ---------------
        // see http://issues.apache.org/jira/browse/LANG-59
        //
        // Manually truncate milliseconds, seconds and minutes, rather than
        // using
        // Calendar methods.

        Date date = val.getTime();
        long time = date.getTime();
        boolean done = false;

        // truncate milliseconds
        int millisecs = val.get(Calendar.MILLISECOND);
        if (MODIFY_TRUNCATE == modType || millisecs < 500) {
            time = time - millisecs;
        }
        if (field == Calendar.SECOND) {
            done = true;
        }

        // truncate seconds
        int seconds = val.get(Calendar.SECOND);
        if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) {
            time = time - (seconds * 1000L);
        }
        if (field == Calendar.MINUTE) {
            done = true;
        }

        // truncate minutes
        int minutes = val.get(Calendar.MINUTE);
        if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) {
            time = time - (minutes * 60000L);
        }

        // reset time
        if (date.getTime() != time) {
            date.setTime(time);
            val.setTime(date);
        }
        // ----------------- Fix for LANG-59 ----------------------- END
        // ----------------

        boolean roundUp = false;
        for (int i = 0; i < fields.length; i++) {
            for (int j = 0; j < fields[i].length; j++) {
                if (fields[i][j] == field) {
                    // This is our field... we stop looping
                    if (modType == MODIFY_CEILING
                            || (modType == MODIFY_ROUND && roundUp)) {
                        if (field == SEMI_MONTH) {
                            // This is a special case that's hard to generalize
                            // If the date is 1, we round up to 16, otherwise
                            // we subtract 15 days and add 1 month
                            if (val.get(Calendar.DATE) == 1) {
                                val.add(Calendar.DATE, 15);
                            } else {
                                val.add(Calendar.DATE, -15);
                                val.add(Calendar.MONTH, 1);
                            }
                            // ----------------- Fix for LANG-440
                            // ---------------------- START ---------------
                        } else if (field == Calendar.AM_PM) {
                            // This is a special case
                            // If the time is 0, we round up to 12, otherwise
                            // we subtract 12 hours and add 1 day
                            if (val.get(Calendar.HOUR_OF_DAY) == 0) {
                                val.add(Calendar.HOUR_OF_DAY, 12);
                            } else {
                                val.add(Calendar.HOUR_OF_DAY, -12);
                                val.add(Calendar.DATE, 1);
                            }
                            // ----------------- Fix for LANG-440
                            // ---------------------- END ---------------
                        } else {
                            // We need at add one to this field since the
                            // last number causes us to round up
                            val.add(fields[i][0], 1);
                        }
                    }
                    return;
                }
            }
            // We have various fields that are not easy roundings
            int offset = 0;
            boolean offsetSet = false;
            // These are special types of fields that require different rounding
            // rules
            switch (field) {
                case SEMI_MONTH:
                    if (fields[i][0] == Calendar.DATE) {
                        // If we're going to drop the DATE field's value,
                        // we want to do this our own way.
                        // We need to subtrace 1 since the date has a minimum of
                        // 1
                        offset = val.get(Calendar.DATE) - 1;
                        // If we're above 15 days adjustment, that means we're
                        // in
                        // the
                        // bottom half of the month and should stay accordingly.
                        if (offset >= 15) {
                            offset -= 15;
                        }
                        // Record whether we're in the top or bottom half of
                        // that
                        // range
                        roundUp = offset > 7;
                        offsetSet = true;
                    }
                    break;
                case Calendar.AM_PM:
                    if (fields[i][0] == Calendar.HOUR_OF_DAY) {
                        // If we're going to drop the HOUR field's value,
                        // we want to do this our own way.
                        offset = val.get(Calendar.HOUR_OF_DAY);
                        if (offset >= 12) {
                            offset -= 12;
                        }
                        roundUp = offset >= 6;
                        offsetSet = true;
                    }
                    break;
            }
            if (!offsetSet) {
                int min = val.getActualMinimum(fields[i][0]);
                int max = val.getActualMaximum(fields[i][0]);
                // Calculate the offset from the minimum allowed value
                offset = val.get(fields[i][0]) - min;
                // Set roundUp if this is more than half way between the minimum
                // and maximum
                roundUp = offset > ((max - min) / 2);
            }
            // We need to remove this field
            if (offset != 0) {
                val.set(fields[i][0], val.get(fields[i][0]) - offset);
            }
        }
        throw new IllegalArgumentException("The field " + field
                + " is not supported");

    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * This constructs an <code>Iterator</code> over each day in a date range
     * defined by a focus date and range style.
     * </p>
     * 
     * <p>
     * For instance, passing Thursday, July 4, 2002 and a
     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> that
     * starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 2002,
     * returning a Calendar instance for each intermediate day.
     * </p>
     * 
     * <p>
     * This method provides an iterator that returns Calendar objects. The days
     * are progressed using {@link Calendar#add(int, int)}.
     * </p>
     * 
     * @param focus
     *            the date to work with, not null
     * @param rangeStyle
     *            the style constant to use. Must be one of
     *            {@link DateUtils#RANGE_MONTH_SUNDAY},
     *            {@link DateUtils#RANGE_MONTH_MONDAY},
     *            {@link DateUtils#RANGE_WEEK_SUNDAY},
     *            {@link DateUtils#RANGE_WEEK_MONDAY},
     *            {@link DateUtils#RANGE_WEEK_RELATIVE},
     *            {@link DateUtils#RANGE_WEEK_CENTER}
     * @return the date iterator, which always returns Calendar instances
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws IllegalArgumentException
     *             if the rangeStyle is invalid
     */
    public static Iterator<Calendar> iteratorCalendar(Date focus, int rangeStyle) {
        if (focus == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar gval = Calendar.getInstance();
        gval.setTime(focus);
        return iteratorCalendar(gval, rangeStyle);
    }

    /**
     * <p>
     * This constructs an <code>Iterator</code> over each day in a date range
     * defined by a focus date and range style.
     * </p>
     * 
     * <p>
     * For instance, passing Thursday, July 4, 2002 and a
     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> that
     * starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 2002,
     * returning a Calendar instance for each intermediate day.
     * </p>
     * 
     * <p>
     * This method provides an iterator that returns Calendar objects. The days
     * are progressed using {@link Calendar#add(int, int)}.
     * </p>
     * 
     * @param focus
     *            the date to work with
     * @param rangeStyle
     *            the style constant to use. Must be one of
     *            {@link DateUtils#RANGE_MONTH_SUNDAY},
     *            {@link DateUtils#RANGE_MONTH_MONDAY},
     *            {@link DateUtils#RANGE_WEEK_SUNDAY},
     *            {@link DateUtils#RANGE_WEEK_MONDAY},
     *            {@link DateUtils#RANGE_WEEK_RELATIVE},
     *            {@link DateUtils#RANGE_WEEK_CENTER}
     * @return the date iterator
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws IllegalArgumentException
     *             if the rangeStyle is invalid
     */
    public static Iterator<Calendar> iteratorCalendar(Calendar focus,
            int rangeStyle) {
        if (focus == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar start = null;
        Calendar end = null;
        int startCutoff = Calendar.SUNDAY;
        int endCutoff = Calendar.SATURDAY;
        switch (rangeStyle) {
            case RANGE_MONTH_SUNDAY:
            case RANGE_MONTH_MONDAY:
                // Set start to the first of the month
                start = truncateCalendar(focus, Calendar.MONTH);
                // Set end to the last of the month
                end = (Calendar) start.clone();
                end.add(Calendar.MONTH, 1);
                end.add(Calendar.DATE, -1);
                // Loop start back to the previous sunday or monday
                if (rangeStyle == RANGE_MONTH_MONDAY) {
                    startCutoff = Calendar.MONDAY;
                    endCutoff = Calendar.SUNDAY;
                }
                break;
            case RANGE_WEEK_SUNDAY:
            case RANGE_WEEK_MONDAY:
            case RANGE_WEEK_RELATIVE:
            case RANGE_WEEK_CENTER:
                // Set start and end to the current date
                start = truncateCalendar(focus, Calendar.DATE);
                end = truncateCalendar(focus, Calendar.DATE);
                switch (rangeStyle) {
                    case RANGE_WEEK_SUNDAY:
                        // already set by default
                        break;
                    case RANGE_WEEK_MONDAY:
                        startCutoff = Calendar.MONDAY;
                        endCutoff = Calendar.SUNDAY;
                        break;
                    case RANGE_WEEK_RELATIVE:
                        startCutoff = focus.get(Calendar.DAY_OF_WEEK);
                        endCutoff = startCutoff - 1;
                        break;
                    case RANGE_WEEK_CENTER:
                        startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
                        endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
                        break;
                }
                break;
            default:
                throw new IllegalArgumentException("The range style "
                        + rangeStyle + " is not valid.");
        }
        if (startCutoff < Calendar.SUNDAY) {
            startCutoff += 7;
        }
        if (startCutoff > Calendar.SATURDAY) {
            startCutoff -= 7;
        }
        if (endCutoff < Calendar.SUNDAY) {
            endCutoff += 7;
        }
        if (endCutoff > Calendar.SATURDAY) {
            endCutoff -= 7;
        }
        while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
            start.add(Calendar.DATE, -1);
        }
        while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
            end.add(Calendar.DATE, 1);
        }
        return new DateIterator(start, end);
    }

    /**
     * <p>
     * This constructs an <code>Iterator</code> over each day in a date range
     * defined by a focus date and range style.
     * </p>
     * 
     * <p>
     * For instance, passing Thursday, July 4, 2002 and a
     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> that
     * starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 2002,
     * returning a Calendar instance for each intermediate day.
     * </p>
     * 
     * @param focus
     *            the date to work with, either <code>Date</code> or
     *            <code>Calendar</code>
     * @param rangeStyle
     *            the style constant to use. Must be one of the range styles
     *            listed for the {@link #iteratorDateOrCalender(Calendar, int)}
     *            method.
     * @return the date iterator
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     * @throws ClassCastException
     *             if the object type is not a <code>Date</code> or
     *             <code>Calendar</code>
     */
    public static Iterator<?> iteratorDateOrCalender(Object focus,
            int rangeStyle) {
        if (focus == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        if (focus instanceof Date) {
            return iteratorCalendar((Date) focus, rangeStyle);
        } else if (focus instanceof Calendar) {
            return iteratorDateOrCalender((Calendar) focus, rangeStyle);
        } else {
            throw new ClassCastException("Could not iterate based on " + focus);
        }
    }

    /**
     * <p>
     * Returns the number of milliseconds within the fragment. All datefields
     * greater than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the milliseconds of any date will only return the number of
     * milliseconds of the current second (resulting in a number between 0 and
     * 999). This method will retrieve the number of milliseconds for any
     * fragment. For example, if you want to calculate the number of
     * milliseconds past today, your fragment is Calendar.DATE or
     * Calendar.DAY_OF_YEAR. The result will be all milliseconds of the past
     * hour(s), minutes(s) and second(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a SECOND field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will
     * return 538</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will
     * return 538</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10538 (10*1000 + 538)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in milliseconds)</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @return number of milliseconds within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInMilliseconds(Date date, int fragment) {
        return getFragmentDate(date, fragment, Calendar.MILLISECOND);
    }

    /**
     * <p>
     * Returns the number of seconds within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the seconds of any date will only return the number of seconds of
     * the current minute (resulting in a number between 0 and 59). This method
     * will retrieve the number of seconds for any fragment. For example, if you
     * want to calculate the number of seconds past today, your fragment is
     * Calendar.DATE or Calendar.DAY_OF_YEAR. The result will be all seconds of
     * the past hour(s) and minutes(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a SECOND field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10 (equivalent to deprecated date.getSeconds())</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10 (equivalent to deprecated date.getSeconds())</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 26110 (7*3600 + 15*60 + 10)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in seconds)</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @return number of seconds within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInSeconds(Date date, int fragment) {
        return getFragmentDate(date, fragment, Calendar.SECOND);
    }

    /**
     * <p>
     * Returns the number of minutes within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the minutes of any date will only return the number of minutes of
     * the current hour (resulting in a number between 0 and 59). This method
     * will retrieve the number of minutes for any fragment. For example, if you
     * want to calculate the number of minutes past this month, your fragment is
     * Calendar.MONTH. The result will be all minutes of the past day(s) and
     * hour(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a MINUTE field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment
     * will return 15 (equivalent to deprecated date.getMinutes())</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment
     * will return 15 (equivalent to deprecated date.getMinutes())</li>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 15</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 435 (7*60 + 15)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in minutes)</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @return number of minutes within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInMinutes(Date date, int fragment) {
        return getFragmentDate(date, fragment, Calendar.MINUTE);
    }

    /**
     * <p>
     * Returns the number of hours within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the hours of any date will only return the number of hours of the
     * current day (resulting in a number between 0 and 23). This method will
     * retrieve the number of hours for any fragment. For example, if you want
     * to calculate the number of hours past this month, your fragment is
     * Calendar.MONTH. The result will be all hours of the past day(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a HOUR field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 7 (equivalent to deprecated date.getHours())</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 7 (equivalent to deprecated date.getHours())</li>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 7</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 127 (5*24 + 7)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in hours)</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @return number of hours within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInHours(Date date, int fragment) {
        return getFragmentDate(date, fragment, Calendar.HOUR_OF_DAY);
    }

    /**
     * <p>
     * Returns the number of days within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the days of any date will only return the number of days of the
     * current month (resulting in a number between 1 and 31). This method will
     * retrieve the number of days for any fragment. For example, if you want to
     * calculate the number of days past this year, your fragment is
     * Calendar.YEAR. The result will be all days of the past month(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a DAY field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
     * (equivalent to deprecated date.getDay())</li>
     * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
     * (equivalent to deprecated date.getDay())</li>
     * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
     * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
     * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
     * (a millisecond cannot be split in days)</li>
     * </ul>
     * </p>
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @return number of days within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInDays(Date date, int fragment) {
        return getFragmentDate(date, fragment, Calendar.DAY_OF_YEAR);
    }

    /**
     * <p>
     * Returns the number of milliseconds within the fragment. All datefields
     * greater than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the milliseconds of any date will only return the number of
     * milliseconds of the current second (resulting in a number between 0 and
     * 999). This method will retrieve the number of milliseconds for any
     * fragment. For example, if you want to calculate the number of seconds
     * past today, your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The
     * result will be all seconds of the past hour(s), minutes(s) and second(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a MILLISECOND field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will
     * return 538 (equivalent to calendar.get(Calendar.MILLISECOND))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will
     * return 538 (equivalent to calendar.get(Calendar.MILLISECOND))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10538 (10*1000 + 538)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in milliseconds)</li>
     * </ul>
     * </p>
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @return number of milliseconds within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInMilliseconds(Calendar calendar, int fragment) {
        return getFragmentCalendar(calendar, fragment, Calendar.MILLISECOND);
    }

    /**
     * <p>
     * Returns the number of seconds within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the seconds of any date will only return the number of seconds of
     * the current minute (resulting in a number between 0 and 59). This method
     * will retrieve the number of seconds for any fragment. For example, if you
     * want to calculate the number of seconds past today, your fragment is
     * Calendar.DATE or Calendar.DAY_OF_YEAR. The result will be all seconds of
     * the past hour(s) and minutes(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a SECOND field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10 (equivalent to calendar.get(Calendar.SECOND))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will
     * return 10 (equivalent to calendar.get(Calendar.SECOND))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 26110 (7*3600 + 15*60 + 10)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in seconds)</li>
     * </ul>
     * </p>
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @return number of seconds within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInSeconds(Calendar calendar, int fragment) {
        return getFragmentCalendar(calendar, fragment, Calendar.SECOND);
    }

    /**
     * <p>
     * Returns the number of minutes within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the minutes of any date will only return the number of minutes of
     * the current hour (resulting in a number between 0 and 59). This method
     * will retrieve the number of minutes for any fragment. For example, if you
     * want to calculate the number of minutes past this month, your fragment is
     * Calendar.MONTH. The result will be all minutes of the past day(s) and
     * hour(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a MINUTE field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment
     * will return 15 (equivalent to calendar.get(Calendar.MINUTES))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment
     * will return 15 (equivalent to calendar.get(Calendar.MINUTES))</li>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 15</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 435 (7*60 + 15)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in minutes)</li>
     * </ul>
     * </p>
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @return number of minutes within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInMinutes(Calendar calendar, int fragment) {
        return getFragmentCalendar(calendar, fragment, Calendar.MINUTE);
    }

    /**
     * <p>
     * Returns the number of hours within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the hours of any date will only return the number of hours of the
     * current day (resulting in a number between 0 and 23). This method will
     * retrieve the number of hours for any fragment. For example, if you want
     * to calculate the number of hours past this month, your fragment is
     * Calendar.MONTH. The result will be all hours of the past day(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a HOUR field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 7 (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment
     * will return 7 (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
     * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 7</li>
     * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will
     * return 127 (5*24 + 7)</li>
     * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment
     * will return 0 (a millisecond cannot be split in hours)</li>
     * </ul>
     * </p>
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @return number of hours within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInHours(Calendar calendar, int fragment) {
        return getFragmentCalendar(calendar, fragment, Calendar.HOUR_OF_DAY);
    }

    /**
     * <p>
     * Returns the number of days within the fragment. All datefields greater
     * than the fragment will be ignored.
     * </p>
     * 
     * <p>
     * Asking the days of any date will only return the number of days of the
     * current month (resulting in a number between 1 and 31). This method will
     * retrieve the number of days for any fragment. For example, if you want to
     * calculate the number of days past this year, your fragment is
     * Calendar.YEAR. The result will be all days of the past month(s).
     * </p>
     * 
     * <p>
     * Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND A fragment less
     * than or equal to a DAY field will return 0.
     * </p>
     * 
     * <p>
     * <ul>
     * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
     * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
     * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
     * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
     * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
     * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
     * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
     * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
     * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
     * (a millisecond cannot be split in days)</li>
     * </ul>
     * </p>
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @return number of days within the fragment of date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    public static long getFragmentInDays(Calendar calendar, int fragment) {
        return getFragmentCalendar(calendar, fragment, Calendar.DAY_OF_YEAR);
    }

    /**
     * Date-version for fragment-calculation in any unit
     * 
     * @param date
     *            the date to work with, not null
     * @param fragment
     *            the Calendar field part of date to calculate
     * @param unit
     *            Calendar field defining the unit
     * @return number of units within the fragment of the date
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    private static long getFragmentDate(Date date, int fragment, int unit) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return getFragmentCalendar(calendar, fragment, unit);
    }

    /**
     * Calendar-version for fragment-calculation in any unit
     * 
     * @param calendar
     *            the calendar to work with, not null
     * @param fragment
     *            the Calendar field part of calendar to calculate
     * @param unit
     *            Calendar field defining the unit
     * @return number of units within the fragment of the calendar
     * @throws IllegalArgumentException
     *             if the date is <code>null</code> or fragment is not supported
     * @since 2.4
     */
    private static long getFragmentCalendar(Calendar calendar, int fragment,
            int unit) {
        if (calendar == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        long millisPerUnit = getMillisPerUnit(unit);
        long result = 0;

        // Fragments bigger than a day require a breakdown to days
        switch (fragment) {
            case Calendar.YEAR:
                result += (calendar.get(Calendar.DAY_OF_YEAR) * MILLIS_PER_DAY)
                        / millisPerUnit;
                break;
            case Calendar.MONTH:
                result += (calendar.get(Calendar.DAY_OF_MONTH) * MILLIS_PER_DAY)
                        / millisPerUnit;
                break;
        }

        switch (fragment) {
        // Number of days already calculated for these cases
            case Calendar.YEAR:
            case Calendar.MONTH:

                // The rest of the valid cases
            case Calendar.DAY_OF_YEAR:
            case Calendar.DATE:
                result += (calendar.get(Calendar.HOUR_OF_DAY) * MILLIS_PER_HOUR)
                        / millisPerUnit;
                //$FALL-THROUGH$
            case Calendar.HOUR_OF_DAY:
                result += (calendar.get(Calendar.MINUTE) * MILLIS_PER_MINUTE)
                        / millisPerUnit;
                //$FALL-THROUGH$
            case Calendar.MINUTE:
                result += (calendar.get(Calendar.SECOND) * MILLIS_PER_SECOND)
                        / millisPerUnit;
                //$FALL-THROUGH$
            case Calendar.SECOND:
                result += (calendar.get(Calendar.MILLISECOND) * 1)
                        / millisPerUnit;
                break;
            case Calendar.MILLISECOND:
                break;// never useful
            default:
                throw new IllegalArgumentException("The fragment " + fragment
                        + " is not supported");
        }
        return result;
    }

    /**
     * Determines if two calendars are equal up to no more than the specified
     * most significant field.
     * 
     * @param cal1
     *            the first calendar, not <code>null</code>
     * @param cal2
     *            the second calendar, not <code>null</code>
     * @param field
     *            the field from <code>Calendar</code>
     * @return <code>true</code> if equal; otherwise <code>false</code>
     * @throws IllegalArgumentException
     *             if any argument is <code>null</code>
     * @see #truncateCalendar(Calendar, int)
     * @see #truncatedEquals(Date, Date, int)
     * @since 3.0
     */
    public static boolean truncatedEquals(Calendar cal1, Calendar cal2,
            int field) {
        return truncatedCompareTo(cal1, cal2, field) == 0;
    }

    /**
     * Determines if two dates are equal up to no more than the specified most
     * significant field.
     * 
     * @param date1
     *            the first date, not <code>null</code>
     * @param date2
     *            the second date, not <code>null</code>
     * @param field
     *            the field from <code>Calendar</code>
     * @return <code>true</code> if equal; otherwise <code>false</code>
     * @throws IllegalArgumentException
     *             if any argument is <code>null</code>
     * @see #truncateDate(Date, int)
     * @see #truncatedEquals(Calendar, Calendar, int)
     * @since 3.0
     */
    public static boolean truncatedEquals(Date date1, Date date2, int field) {
        return truncatedCompareTo(date1, date2, field) == 0;
    }

    /**
     * Determines how two calendars compare up to no more than the specified
     * most significant field.
     * 
     * @param cal1
     *            the first calendar, not <code>null</code>
     * @param cal2
     *            the second calendar, not <code>null</code>
     * @param field
     *            the field from <code>Calendar</code>
     * @return a negative integer, zero, or a positive integer as the first
     *         calendar is less than, equal to, or greater than the second.
     * @throws IllegalArgumentException
     *             if any argument is <code>null</code>
     * @see #truncateCalendar(Calendar, int)
     * @see #truncatedCompareTo(Date, Date, int)
     * @since 3.0
     */
    public static int truncatedCompareTo(Calendar cal1, Calendar cal2, int field) {
        Calendar truncatedCal1 = truncateCalendar(cal1, field);
        Calendar truncatedCal2 = truncateCalendar(cal2, field);
        return truncatedCal1.compareTo(truncatedCal2);
    }

    /**
     * Determines how two dates compare up to no more than the specified most
     * significant field.
     * 
     * @param date1
     *            the first date, not <code>null</code>
     * @param date2
     *            the second date, not <code>null</code>
     * @param field
     *            the field from <code>Calendar</code>
     * @return a negative integer, zero, or a positive integer as the first date
     *         is less than, equal to, or greater than the second.
     * @throws IllegalArgumentException
     *             if any argument is <code>null</code>
     * @see #truncateCalendar(Calendar, int)
     * @see #truncatedCompareTo(Date, Date, int)
     * @since 3.0
     */
    public static int truncatedCompareTo(Date date1, Date date2, int field) {
        Date truncatedDate1 = truncateDate(date1, field);
        Date truncatedDate2 = truncateDate(date2, field);
        return truncatedDate1.compareTo(truncatedDate2);
    }

    /**
     * Returns the number of millis of a datefield, if this is a constant value
     * 
     * @param unit
     *            A Calendar field which is a valid unit for a fragment
     * @return number of millis
     * @throws IllegalArgumentException
     *             if date can't be represented in millisenconds
     * @since 2.4
     */
    private static long getMillisPerUnit(int unit) {
        long result = Long.MAX_VALUE;
        switch (unit) {
            case Calendar.DAY_OF_YEAR:
            case Calendar.DATE:
                result = MILLIS_PER_DAY;
                break;
            case Calendar.HOUR_OF_DAY:
                result = MILLIS_PER_HOUR;
                break;
            case Calendar.MINUTE:
                result = MILLIS_PER_MINUTE;
                break;
            case Calendar.SECOND:
                result = MILLIS_PER_SECOND;
                break;
            case Calendar.MILLISECOND:
                result = 1;
                break;
            default:
                throw new IllegalArgumentException("The unit " + unit
                        + " cannot be represented is milleseconds");
        }
        return result;
    }

    /**
     * <p>
     * Date iterator.
     * </p>
     */
    static class DateIterator implements Iterator<Calendar> {
        private final Calendar endFinal;
        private final Calendar spot;

        /**
         * Constructs a DateIterator that ranges from one date to another.
         * 
         * @param startFinal
         *            start date (inclusive)
         * @param endFinal
         *            end date (not inclusive)
         */
        DateIterator(Calendar startFinal, Calendar endFinal) {
            super();
            this.endFinal = endFinal;
            spot = startFinal;
            spot.add(Calendar.DATE, -1);
        }

        /**
         * Has the iterator not reached the end date yet?
         * 
         * @return <code>true</code> if the iterator has yet to reach the end
         *         date
         */
        public boolean hasNext() {
            return spot.before(endFinal);
        }

        /**
         * Return the next calendar in the iteration
         * 
         * @return Object calendar for the next date
         */
        public Calendar next() {
            if (spot.equals(endFinal)) {
                throw new NoSuchElementException();
            }
            spot.add(Calendar.DATE, 1);
            return (Calendar) spot.clone();
        }

        /**
         * Always throws UnsupportedOperationException.
         * 
         * @throws UnsupportedOperationException
         * @see java.util.Iterator#remove()
         */
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    // ///////////////////////////////////////////
    //
    // DateFormatUtils
    //
    // ///////////////////////////////////////////

    /**
     * ISO8601 formatter for date-time without time zone. The format used is
     * <tt>yyyy-MM-dd'T'HH:mm:ss</tt>.
     */
    public static final FastDateFormat ISO_DATETIME_FORMAT = FastDateFormat
            .getInstance("yyyy-MM-dd'T'HH:mm:ss");

    /**
     * ISO8601 formatter for date-time with time zone. The format used is
     * <tt>yyyy-MM-dd'T'HH:mm:ssZZ</tt>.
     */
    public static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT = FastDateFormat
            .getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");

    /**
     * ISO8601 formatter for date without time zone. The format used is
     * <tt>yyyy-MM-dd</tt>.
     */
    public static final FastDateFormat ISO_DATE_FORMAT = FastDateFormat
            .getInstance("yyyy-MM-dd");

    /**
     * ISO8601-like formatter for date with time zone. The format used is
     * <tt>yyyy-MM-ddZZ</tt>. This pattern does not comply with the formal
     * ISO8601 specification as the standard does not allow a time zone without
     * a time.
     */
    public static final FastDateFormat ISO_DATE_TIME_ZONE_FORMAT = FastDateFormat
            .getInstance("yyyy-MM-ddZZ");

    /**
     * ISO8601 formatter for time without time zone. The format used is
     * <tt>'T'HH:mm:ss</tt>.
     */
    public static final FastDateFormat ISO_TIME_FORMAT = FastDateFormat
            .getInstance("'T'HH:mm:ss");

    /**
     * ISO8601 formatter for time with time zone. The format used is
     * <tt>'T'HH:mm:ssZZ</tt>.
     */
    public static final FastDateFormat ISO_TIME_TIME_ZONE_FORMAT = FastDateFormat
            .getInstance("'T'HH:mm:ssZZ");

    /**
     * ISO8601-like formatter for time without time zone. The format used is
     * <tt>HH:mm:ss</tt>. This pattern does not comply with the formal ISO8601
     * specification as the standard requires the 'T' prefix for times.
     */
    public static final FastDateFormat ISO_TIME_NO_T_FORMAT = FastDateFormat
            .getInstance("HH:mm:ss");

    /**
     * ISO8601-like formatter for time with time zone. The format used is
     * <tt>HH:mm:ssZZ</tt>. This pattern does not comply with the formal ISO8601
     * specification as the standard requires the 'T' prefix for times.
     */
    public static final FastDateFormat ISO_TIME_NO_T_TIME_ZONE_FORMAT = FastDateFormat
            .getInstance("HH:mm:ssZZ");

    /**
     * SMTP (and probably other) date headers. The format used is
     * <tt>EEE, dd MMM yyyy HH:mm:ss Z</tt> in US locale.
     */
    public static final FastDateFormat SMTP_DATETIME_FORMAT = FastDateFormat
            .getInstance("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);

    /**
     * <p>
     * Formats a date/time into a specific pattern using the UTC time zone.
     * </p>
     * 
     * @param millis
     *            the date to format expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @return the formatted date
     */
    public static String formatUTC(long millis, String pattern) {
        return formatDate(new Date(millis), pattern, UTC_TIME_ZONE, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern using the UTC time zone.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @return the formatted date
     */
    public static String formatUTC(Date date, String pattern) {
        return formatDate(date, pattern, UTC_TIME_ZONE, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern using the UTC time zone.
     * </p>
     * 
     * @param millis
     *            the date to format expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatUTC(long millis, String pattern, Locale locale) {
        return formatDate(new Date(millis), pattern, UTC_TIME_ZONE, locale);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern using the UTC time zone.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatUTC(Date date, String pattern, Locale locale) {
        return formatDate(date, pattern, UTC_TIME_ZONE, locale);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern.
     * </p>
     * 
     * @param millis
     *            the date to format expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @return the formatted date
     */
    public static String formatDate(long millis, String pattern) {
        return formatDate(new Date(millis), pattern, null, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @return the formatted date
     */
    public static String formatDate(Date date, String pattern) {
        return formatDate(date, pattern, null, null);
    }

    /**
     * <p>
     * Formats a calendar into a specific pattern.
     * </p>
     * 
     * @param calendar
     *            the calendar to format
     * @param pattern
     *            the pattern to use to format the calendar
     * @return the formatted calendar
     * @see FastDateFormat#format(Calendar)
     * @since 2.4
     */
    public static String formatDate(Calendar calendar, String pattern) {
        return formatDate(calendar, pattern, null, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a time zone.
     * </p>
     * 
     * @param millis
     *            the time expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(long millis, String pattern,
            TimeZone timeZone) {
        return formatDate(new Date(millis), pattern, timeZone, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a time zone.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(Date date, String pattern, TimeZone timeZone) {
        return formatDate(date, pattern, timeZone, null);
    }

    /**
     * <p>
     * Formats a calendar into a specific pattern in a time zone.
     * </p>
     * 
     * @param calendar
     *            the calendar to format
     * @param pattern
     *            the pattern to use to format the calendar
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @return the formatted calendar
     * @see FastDateFormat#format(Calendar)
     * @since 2.4
     */
    public static String formatDate(Calendar calendar, String pattern,
            TimeZone timeZone) {
        return formatDate(calendar, pattern, timeZone, null);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a locale.
     * </p>
     * 
     * @param millis
     *            the date to format expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(long millis, String pattern, Locale locale) {
        return formatDate(new Date(millis), pattern, null, locale);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a locale.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(Date date, String pattern, Locale locale) {
        return formatDate(date, pattern, null, locale);
    }

    /**
     * <p>
     * Formats a calendar into a specific pattern in a locale.
     * </p>
     * 
     * @param calendar
     *            the calendar to format
     * @param pattern
     *            the pattern to use to format the calendar
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted calendar
     * @see FastDateFormat#format(Calendar)
     * @since 2.4
     */
    public static String formatDate(Calendar calendar, String pattern,
            Locale locale) {
        return formatDate(calendar, pattern, null, locale);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a time zone and locale.
     * </p>
     * 
     * @param millis
     *            the date to format expressed in milliseconds
     * @param pattern
     *            the pattern to use to format the date
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(long millis, String pattern,
            TimeZone timeZone, Locale locale) {
        return formatDate(new Date(millis), pattern, timeZone, locale);
    }

    /**
     * <p>
     * Formats a date/time into a specific pattern in a time zone and locale.
     * </p>
     * 
     * @param date
     *            the date to format
     * @param pattern
     *            the pattern to use to format the date
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted date
     */
    public static String formatDate(Date date, String pattern,
            TimeZone timeZone, Locale locale) {
        FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone,
                locale);
        return df.format(date);
    }

    /**
     * <p>
     * Formats a calendar into a specific pattern in a time zone and locale.
     * </p>
     * 
     * @param calendar
     *            the calendar to format
     * @param pattern
     *            the pattern to use to format the calendar
     * @param timeZone
     *            the time zone to use, may be <code>null</code>
     * @param locale
     *            the locale to use, may be <code>null</code>
     * @return the formatted calendar
     * @see FastDateFormat#format(Calendar)
     * @since 2.4
     */
    public static String formatDate(Calendar calendar, String pattern,
            TimeZone timeZone, Locale locale) {
        FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone,
                locale);
        return df.format(calendar);
    }

    // ///////////////////////////////////////////
    //
    // DurationFormatUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Pattern used with <code>FastDateFormat</code> and
     * <code>SimpleDateFormat</code> for the ISO8601 period format used in
     * durations.
     * </p>
     * 
     * @see FastDateFormat
     * @see java.text.SimpleDateFormat
     */
    public static final String ISO_EXTENDED_FORMAT_PATTERN = "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.S'S'";

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Formats the time gap as a string.
     * </p>
     * 
     * <p>
     * The format used is ISO8601-like: <i>H</i>:<i>m</i>:<i>s</i>.<i>S</i>.
     * </p>
     * 
     * @param durationMillis
     *            the duration to format
     * @return the time as a String
     */
    public static String formatDurationHMS(long durationMillis) {
        return formatDuration(durationMillis, "H:mm:ss.SSS");
    }

    /**
     * <p>
     * Formats the time gap as a string.
     * </p>
     * 
     * <p>
     * The format used is the ISO8601 period format.
     * </p>
     * 
     * <p>
     * This method formats durations using the days and lower fields of the ISO
     * format pattern, such as P7D6TH5M4.321S.
     * </p>
     * 
     * @param durationMillis
     *            the duration to format
     * @return the time as a String
     */
    public static String formatDurationISO(long durationMillis) {
        return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN,
                false);
    }

    /**
     * <p>
     * Formats the time gap as a string, using the specified format, and padding
     * with zeros and using the default timezone.
     * </p>
     * 
     * <p>
     * This method formats durations using the days and lower fields of the
     * format pattern. Months and larger are not used.
     * </p>
     * 
     * @param durationMillis
     *            the duration to format
     * @param format
     *            the way in which to format the duration
     * @return the time as a String
     */
    public static String formatDuration(long durationMillis, String format) {
        return formatDuration(durationMillis, format, true);
    }

    /**
     * <p>
     * Formats the time gap as a string, using the specified format. Padding the
     * left hand side of numbers with zeroes is optional and the timezone may be
     * specified.
     * </p>
     * 
     * <p>
     * This method formats durations using the days and lower fields of the
     * format pattern. Months and larger are not used.
     * </p>
     * 
     * @param durationMillis
     *            the duration to format
     * @param format
     *            the way in which to format the duration
     * @param padWithZeros
     *            whether to pad the left hand side of numbers with 0's
     * @return the time as a String
     */
    public static String formatDuration(long durationMillis, String format,
            boolean padWithZeros) {

        DurationToken[] tokens = lexx(format);

        int days = 0;
        int hours = 0;
        int minutes = 0;
        int seconds = 0;
        int milliseconds = 0;

        if (DurationToken.containsTokenWithValue(tokens, DATE_d)) {
            days = (int) (durationMillis / MILLIS_PER_DAY);
            durationMillis = durationMillis - (days * MILLIS_PER_DAY);
        }
        if (DurationToken.containsTokenWithValue(tokens, DATE_H)) {
            hours = (int) (durationMillis / MILLIS_PER_HOUR);
            durationMillis = durationMillis - (hours * MILLIS_PER_HOUR);
        }
        if (DurationToken.containsTokenWithValue(tokens, DATE_m)) {
            minutes = (int) (durationMillis / MILLIS_PER_MINUTE);
            durationMillis = durationMillis - (minutes * MILLIS_PER_MINUTE);
        }
        if (DurationToken.containsTokenWithValue(tokens, DATE_s)) {
            seconds = (int) (durationMillis / MILLIS_PER_SECOND);
            durationMillis = durationMillis - (seconds * MILLIS_PER_SECOND);
        }
        if (DurationToken.containsTokenWithValue(tokens, DATE_S)) {
            milliseconds = (int) durationMillis;
        }

        return formatPeriodTime(tokens, 0, 0, days, hours, minutes, seconds,
                milliseconds, padWithZeros);
    }

    /**
     * <p>
     * Formats an elapsed time into a plurialization correct string.
     * </p>
     * 
     * <p>
     * This method formats durations using the days and lower fields of the
     * format pattern. Months and larger are not used.
     * </p>
     * 
     * @param durationMillis
     *            the elapsed time to report in milliseconds
     * @param suppressLeadingZeroElements
     *            suppresses leading 0 elements
     * @param suppressTrailingZeroElements
     *            suppresses trailing 0 elements
     * @return the formatted text in days/hours/minutes/seconds
     */
    public static String formatDurationWords(long durationMillis,
            boolean suppressLeadingZeroElements,
            boolean suppressTrailingZeroElements) {

        // This method is generally replacable by the format method, but
        // there are a series of tweaks and special cases that require
        // trickery to replicate.
        String duration = formatDuration(durationMillis,
                "d' days 'H' hours 'm' minutes 's' seconds'");
        if (suppressLeadingZeroElements) {
            // this is a temporary marker on the front. Like ^ in regexp.
            duration = " " + duration;
            String tmp = replaceOnce(duration, " 0 days", "");
            if (tmp.length() != duration.length()) {
                duration = tmp;
                tmp = replaceOnce(duration, " 0 hours", "");
                if (tmp.length() != duration.length()) {
                    duration = tmp;
                    tmp = replaceOnce(duration, " 0 minutes", "");
                    duration = tmp;
                    if (tmp.length() != duration.length()) {
                        duration = replaceOnce(tmp, " 0 seconds", "");
                    }
                }
            }
            if (duration.length() != 0) {
                // strip the space off again
                duration = duration.substring(1);
            }
        }
        if (suppressTrailingZeroElements) {
            String tmp = replaceOnce(duration, " 0 seconds", "");
            if (tmp.length() != duration.length()) {
                duration = tmp;
                tmp = replaceOnce(duration, " 0 minutes", "");
                if (tmp.length() != duration.length()) {
                    duration = tmp;
                    tmp = replaceOnce(duration, " 0 hours", "");
                    if (tmp.length() != duration.length()) {
                        duration = replaceOnce(tmp, " 0 days", "");
                    }
                }
            }
        }
        // handle plurals
        duration = " " + duration;
        duration = replaceOnce(duration, " 1 seconds", " 1 second");
        duration = replaceOnce(duration, " 1 minutes", " 1 minute");
        duration = replaceOnce(duration, " 1 hours", " 1 hour");
        duration = replaceOnce(duration, " 1 days", " 1 day");
        return duration.trim();
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Formats the time gap as a string.
     * </p>
     * 
     * <p>
     * The format used is the ISO8601 period format.
     * </p>
     * 
     * @param startMillis
     *            the start of the duration to format
     * @param endMillis
     *            the end of the duration to format
     * @return the time as a String
     */
    public static String formatPeriodISO(long startMillis, long endMillis) {
        return formatPeriod(startMillis, endMillis,
                ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault());
    }

    /**
     * <p>
     * Formats the time gap as a string, using the specified format. Padding the
     * left hand side of numbers with zeroes is optional.
     * 
     * @param startMillis
     *            the start of the duration
     * @param endMillis
     *            the end of the duration
     * @param format
     *            the way in which to format the duration
     * @return the time as a String
     */
    public static String formatPeriod(long startMillis, long endMillis,
            String format) {
        return formatPeriod(startMillis, endMillis, format, true,
                TimeZone.getDefault());
    }

    /**
     * <p>
     * Formats the time gap as a string, using the specified format. Padding the
     * left hand side of numbers with zeroes is optional and the timezone may be
     * specified.
     * </p>
     * 
     * <p>
     * When calculating the difference between months/days, it chooses to
     * calculate months first. So when working out the number of months and days
     * between January 15th and March 10th, it choose 1 month and 23 days gained
     * by choosing January->February = 1 month and then calculating days
     * forwards, and not the 1 month and 26 days gained by choosing March ->
     * February = 1 month and then calculating days backwards.
     * </p>
     * 
     * <p>
     * For more control, the <a href="http://joda-time.sf.net/">Joda-Time</a>
     * library is recommended.
     * </p>
     * 
     * @param startMillis
     *            the start of the duration
     * @param endMillis
     *            the end of the duration
     * @param format
     *            the way in which to format the duration
     * @param padWithZeros
     *            whether to pad the left hand side of numbers with 0's
     * @param timezone
     *            the millis are defined in
     * @return the time as a String
     */
    public static String formatPeriod(long startMillis, long endMillis,
            String format, boolean padWithZeros, TimeZone timezone) {

        // Used to optimise for differences under 28 days and
        // called formatDuration(millis, format); however this did not work
        // over leap years.
        // TODO: Compare performance to see if anything was lost by
        // losing this optimisation.

        DurationToken[] tokens = lexx(format);

        // timezones get funky around 0, so normalizing everything to GMT
        // stops the hours being off
        Calendar start = Calendar.getInstance(timezone);
        start.setTime(new Date(startMillis));
        Calendar end = Calendar.getInstance(timezone);
        end.setTime(new Date(endMillis));

        // initial estimates
        int milliseconds = end.get(Calendar.MILLISECOND)
                - start.get(Calendar.MILLISECOND);
        int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND);
        int minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE);
        int hours = end.get(Calendar.HOUR_OF_DAY)
                - start.get(Calendar.HOUR_OF_DAY);
        int days = end.get(Calendar.DAY_OF_MONTH)
                - start.get(Calendar.DAY_OF_MONTH);
        int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH);
        int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);

        // each initial estimate is adjusted in case it is under 0
        while (milliseconds < 0) {
            milliseconds += 1000;
            seconds -= 1;
        }
        while (seconds < 0) {
            seconds += 60;
            minutes -= 1;
        }
        while (minutes < 0) {
            minutes += 60;
            hours -= 1;
        }
        while (hours < 0) {
            hours += 24;
            days -= 1;
        }

        if (DurationToken.containsTokenWithValue(tokens, DATE_M)) {
            while (days < 0) {
                days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
                months -= 1;
                start.add(Calendar.MONTH, 1);
            }

            while (months < 0) {
                months += 12;
                years -= 1;
            }

            if (!DurationToken.containsTokenWithValue(tokens, DATE_y)
                    && years != 0) {
                while (years != 0) {
                    months += 12 * years;
                    years = 0;
                }
            }
        } else {
            // there are no M's in the format string

            if (!DurationToken.containsTokenWithValue(tokens, DATE_y)) {
                int target = end.get(Calendar.YEAR);
                if (months < 0) {
                    // target is end-year -1
                    target -= 1;
                }

                while ((start.get(Calendar.YEAR) != target)) {
                    days += start.getActualMaximum(Calendar.DAY_OF_YEAR)
                            - start.get(Calendar.DAY_OF_YEAR);

                    // Not sure I grok why this is needed, but the brutal tests
                    // show it is
                    if (start instanceof GregorianCalendar) {
                        if ((start.get(Calendar.MONTH) == Calendar.FEBRUARY)
                                && (start.get(Calendar.DAY_OF_MONTH) == 29)) {
                            days += 1;
                        }
                    }

                    start.add(Calendar.YEAR, 1);

                    days += start.get(Calendar.DAY_OF_YEAR);
                }

                years = 0;
            }

            while (start.get(Calendar.MONTH) != end.get(Calendar.MONTH)) {
                days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
                start.add(Calendar.MONTH, 1);
            }

            months = 0;

            while (days < 0) {
                days += start.getActualMaximum(Calendar.DAY_OF_MONTH);
                months -= 1;
                start.add(Calendar.MONTH, 1);
            }

        }

        // The rest of this code adds in values that
        // aren't requested. This allows the user to ask for the
        // number of months and get the real count and not just 0->11.

        if (!DurationToken.containsTokenWithValue(tokens, DATE_d)) {
            hours += 24 * days;
            days = 0;
        }
        if (!DurationToken.containsTokenWithValue(tokens, DATE_H)) {
            minutes += 60 * hours;
            hours = 0;
        }
        if (!DurationToken.containsTokenWithValue(tokens, DATE_m)) {
            seconds += 60 * minutes;
            minutes = 0;
        }
        if (!DurationToken.containsTokenWithValue(tokens, DATE_s)) {
            milliseconds += 1000 * seconds;
            seconds = 0;
        }

        return formatPeriodTime(tokens, years, months, days, hours, minutes,
                seconds, milliseconds, padWithZeros);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * The internal method to do the formatting.
     * </p>
     * 
     * @param tokens
     *            the tokens
     * @param years
     *            the number of years
     * @param months
     *            the number of months
     * @param days
     *            the number of days
     * @param hours
     *            the number of hours
     * @param minutes
     *            the number of minutes
     * @param seconds
     *            the number of seconds
     * @param milliseconds
     *            the number of millis
     * @param padWithZeros
     *            whether to pad
     * @return the formatted string
     */
    static String formatPeriodTime(DurationToken[] tokens, int years,
            int months, int days, int hours, int minutes, int seconds,
            int milliseconds, boolean padWithZeros) {
        StringBuffer buffer = new StringBuffer();
        boolean lastOutputSeconds = false;
        int sz = tokens.length;
        for (int i = 0; i < sz; i++) {
            DurationToken token = tokens[i];
            Object value = token.getValue();
            int count = token.getCount();
            if (value instanceof StringBuffer) {
                buffer.append(value.toString());
            } else {
                if (value == DATE_y) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(years), count, '0') : Integer
                            .toString(years));
                    lastOutputSeconds = false;
                } else if (value == DATE_M) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(months), count, '0') : Integer
                            .toString(months));
                    lastOutputSeconds = false;
                } else if (value == DATE_d) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(days), count, '0') : Integer
                            .toString(days));
                    lastOutputSeconds = false;
                } else if (value == DATE_H) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(hours), count, '0') : Integer
                            .toString(hours));
                    lastOutputSeconds = false;
                } else if (value == DATE_m) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(minutes), count, '0') : Integer
                            .toString(minutes));
                    lastOutputSeconds = false;
                } else if (value == DATE_s) {
                    buffer.append(padWithZeros ? leftPad(
                            Integer.toString(seconds), count, '0') : Integer
                            .toString(seconds));
                    lastOutputSeconds = true;
                } else if (value == DATE_S) {
                    if (lastOutputSeconds) {
                        milliseconds += 1000;
                        String str = padWithZeros ? leftPad(
                                Integer.toString(milliseconds), count, '0')
                                : Integer.toString(milliseconds);
                        buffer.append(str.substring(1));
                    } else {
                        buffer.append(padWithZeros ? leftPad(
                                Integer.toString(milliseconds), count, '0')
                                : Integer.toString(milliseconds));
                    }
                    lastOutputSeconds = false;
                }
            }
        }
        return buffer.toString();
    }

    static final Object DATE_y = "y";
    static final Object DATE_M = "M";
    static final Object DATE_d = "d";
    static final Object DATE_H = "H";
    static final Object DATE_m = "m";
    static final Object DATE_s = "s";
    static final Object DATE_S = "S";

    /**
     * Parses a classic date format string into Tokens
     * 
     * @param format
     *            to parse
     * @return array of Token[]
     */
    static DurationToken[] lexx(String format) {
        char[] array = format.toCharArray();
        ArrayList<DurationToken> list = new ArrayList<DurationToken>(
                array.length);

        boolean inLiteral = false;
        StringBuffer buffer = null;
        DurationToken previous = null;
        int sz = array.length;
        for (int i = 0; i < sz; i++) {
            char ch = array[i];
            if (inLiteral && ch != '\'') {
                buffer.append(ch); // buffer can't be null if inLiteral is true
                continue;
            }
            Object value = null;
            switch (ch) {
            // TODO: Need to handle escaping of '
                case '\'':
                    if (inLiteral) {
                        buffer = null;
                        inLiteral = false;
                    } else {
                        buffer = new StringBuffer();
                        list.add(new DurationToken(buffer));
                        inLiteral = true;
                    }
                    break;
                case 'y':
                    value = DATE_y;
                    break;
                case 'M':
                    value = DATE_M;
                    break;
                case 'd':
                    value = DATE_d;
                    break;
                case 'H':
                    value = DATE_H;
                    break;
                case 'm':
                    value = DATE_m;
                    break;
                case 's':
                    value = DATE_s;
                    break;
                case 'S':
                    value = DATE_S;
                    break;
                default:
                    if (buffer == null) {
                        buffer = new StringBuffer();
                        list.add(new DurationToken(buffer));
                    }
                    buffer.append(ch);
            }

            if (value != null) {
                if (previous != null && previous.getValue() == value) {
                    previous.increment();
                } else {
                    DurationToken token = new DurationToken(value);
                    list.add(token);
                    previous = token;
                }
                buffer = null;
            }
        }
        return list.toArray(new DurationToken[list.size()]);
    }

    /**
     * Element that is parsed from the format pattern.
     */
    static class DurationToken {

        /**
         * Helper method to determine if a set of tokens contain a value
         * 
         * @param tokens
         *            set to look in
         * @param value
         *            to look for
         * @return boolean <code>true</code> if contained
         */
        static boolean containsTokenWithValue(DurationToken[] tokens,
                Object value) {
            int sz = tokens.length;
            for (int i = 0; i < sz; i++) {
                if (tokens[i].getValue() == value) {
                    return true;
                }
            }
            return false;
        }

        private Object value;
        private int count;

        /**
         * Wraps a token around a value. A value would be something like a 'Y'.
         * 
         * @param value
         *            to wrap
         */
        DurationToken(Object value) {
            this.value = value;
            this.count = 1;
        }

        /**
         * Wraps a token around a repeated number of a value, for example it
         * would store 'yyyy' as a value for y and a count of 4.
         * 
         * @param value
         *            to wrap
         * @param count
         *            to wrap
         */
        DurationToken(Object value, int count) {
            this.value = value;
            this.count = count;
        }

        /**
         * Adds another one of the value
         */
        void increment() {
            count++;
        }

        /**
         * Gets the current number of values represented
         * 
         * @return int number of values represented
         */
        int getCount() {
            return count;
        }

        /**
         * Gets the particular value this token represents.
         * 
         * @return Object value
         */
        Object getValue() {
            return value;
        }

        /**
         * Supports equality of this Token to another Token.
         * 
         * @param obj2
         *            Object to consider equality of
         * @return boolean <code>true</code> if equal
         */
        @Override
        public boolean equals(Object obj2) {
            if (obj2 instanceof DurationToken) {
                DurationToken tok2 = (DurationToken) obj2;
                if (this.value.getClass() != tok2.value.getClass()) {
                    return false;
                }
                if (this.count != tok2.count) {
                    return false;
                }
                if (this.value instanceof StringBuffer) {
                    return this.value.toString().equals(tok2.value.toString());
                } else if (this.value instanceof Number) {
                    return this.value.equals(tok2.value);
                } else {
                    return this.value == tok2.value;
                }
            }
            return false;
        }

        /**
         * Returns a hashcode for the token equal to the hashcode for the
         * token's value. Thus 'TT' and 'TTTT' will have the same hashcode.
         * 
         * @return The hashcode for the token
         */
        @Override
        public int hashCode() {
            return this.value.hashCode();
        }

        /**
         * Represents this token as a String.
         * 
         * @return String representation of the token
         */
        @Override
        public String toString() {
            return repeat(this.value.toString(), this.count);
        }
    }

    // ///////////////////////////////////////////
    //
    // ExceptionUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Used when printing stack frames to denote the start of a wrapped
     * exception.
     * </p>
     * 
     * <p>
     * Package private for accessibility by test suite.
     * </p>
     */
    static final String WRAPPED_MARKER = " [wrapped] ";

    /**
     * <p>
     * The names of methods commonly used to access a wrapped exception.
     * </p>
     */
    // TODO: Remove in Lang 4.0
    private static final String[] CAUSE_METHOD_NAMES = { "getCause",
            "getNextException", "getTargetException", "getException",
            "getSourceException", "getRootCause", "getCausedByException",
            "getNested", "getLinkedException", "getNestedException",
            "getLinkedCause", "getThrowable", };

    /**
     * <p>
     * Introspects the <code>Throwable</code> to obtain the root cause.
     * </p>
     * 
     * <p>
     * This method walks through the exception chain to the last element, "root"
     * of the tree, using {@link #getCause(Throwable)}, and returns that
     * exception.
     * </p>
     * 
     * <p>
     * From version 2.2, this method handles recursive cause structures that
     * might otherwise cause infinite loops. If the throwable parameter has a
     * cause of itself, then null will be returned. If the throwable parameter
     * cause chain loops, the last element in the chain before the loop is
     * returned.
     * </p>
     * 
     * @param throwable
     *            the throwable to get the root cause for, may be null
     * @return the root cause of the <code>Throwable</code>, <code>null</code>
     *         if none found or null throwable input
     */
    public static Throwable getRootCause(Throwable throwable) {
        List<Throwable> list = getThrowableList(throwable);
        return (list.size() < 2 ? null : (Throwable) list.get(list.size() - 1));
    }

    /**
     * <p>
     * Finds a <code>Throwable</code> by method name.
     * </p>
     * 
     * @param throwable
     *            the exception to examine
     * @param methodName
     *            the name of the method to find and invoke
     * @return the wrapped exception, or <code>null</code> if not found
     */
    // TODO: Remove in Lang 4.0
    private static Throwable getCauseUsingMethodName(Throwable throwable,
            String methodName) {
        Method method = null;
        try {
            method = throwable.getClass().getMethod(methodName, (Class[]) null);
        } catch (NoSuchMethodException ignored) {
            // exception ignored
        } catch (SecurityException ignored) {
            // exception ignored
        }

        if (method != null
                && Throwable.class.isAssignableFrom(method.getReturnType())) {
            try {
                return (Throwable) method.invoke(throwable, EMPTY_OBJECT_ARRAY);
            } catch (IllegalAccessException ignored) {
                // exception ignored
            } catch (IllegalArgumentException ignored) {
                // exception ignored
            } catch (InvocationTargetException ignored) {
                // exception ignored
            }
        }
        return null;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Counts the number of <code>Throwable</code> objects in the exception
     * chain.
     * </p>
     * 
     * <p>
     * A throwable without cause will return <code>1</code>. A throwable with
     * one cause will return <code>2</code> and so on. A <code>null</code>
     * throwable will return <code>0</code>.
     * </p>
     * 
     * <p>
     * From version 2.2, this method handles recursive cause structures that
     * might otherwise cause infinite loops. The cause chain is processed until
     * the end is reached, or until the next item in the chain is already in the
     * result set.
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @return the count of throwables, zero if null input
     */
    public static int getThrowableCount(Throwable throwable) {
        return getThrowableList(throwable).size();
    }

    /**
     * <p>
     * Returns the list of <code>Throwable</code> objects in the exception
     * chain.
     * </p>
     * 
     * <p>
     * A throwable without cause will return an array containing one element -
     * the input throwable. A throwable with one cause will return an array
     * containing two elements. - the input throwable and the cause throwable. A
     * <code>null</code> throwable will return an array of size zero.
     * </p>
     * 
     * <p>
     * From version 2.2, this method handles recursive cause structures that
     * might otherwise cause infinite loops. The cause chain is processed until
     * the end is reached, or until the next item in the chain is already in the
     * result set.
     * </p>
     * 
     * @see #getThrowableList(Throwable)
     * @param throwable
     *            the throwable to inspect, may be null
     * @return the array of throwables, never null
     */
    public static Throwable[] getThrowables(Throwable throwable) {
        List<Throwable> list = getThrowableList(throwable);
        return list.toArray(new Throwable[list.size()]);
    }

    /**
     * <p>
     * Returns the list of <code>Throwable</code> objects in the exception
     * chain.
     * </p>
     * 
     * <p>
     * A throwable without cause will return a list containing one element - the
     * input throwable. A throwable with one cause will return a list containing
     * two elements. - the input throwable and the cause throwable. A
     * <code>null</code> throwable will return a list of size zero.
     * </p>
     * 
     * <p>
     * This method handles recursive cause structures that might otherwise cause
     * infinite loops. The cause chain is processed until the end is reached, or
     * until the next item in the chain is already in the result set.
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @return the list of throwables, never null
     * @since Commons Lang 2.2
     */
    public static List<Throwable> getThrowableList(Throwable throwable) {
        List<Throwable> list = new ArrayList<Throwable>();
        while (throwable != null && list.contains(throwable) == false) {
            list.add(throwable);
            throwable = getCause(throwable);
        }
        return list;
    }

    /**
     * <p>
     * Returns the default names used when searching for the cause of an
     * exception.
     * </p>
     * 
     * <p>
     * This may be modified and used in the overloaded getCause(Throwable,
     * String[]) method.
     * </p>
     * 
     * @return cloned array of the default method names
     * @since 3.0
     * @deprecated This feature will be removed in Lang 4.0
     */
    @Deprecated
    public static String[] getDefaultCauseMethodNames() {
        return clone(CAUSE_METHOD_NAMES);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Introspects the <code>Throwable</code> to obtain the cause.
     * </p>
     * 
     * <p>
     * The method searches for methods with specific names that return a
     * <code>Throwable</code> object. This will pick up most wrapping
     * exceptions, including those from JDK 1.4.
     * 
     * <p>
     * The default list searched for are:
     * </p>
     * <ul>
     * <li><code>getCause()</code></li>
     * <li><code>getNextException()</code></li>
     * <li><code>getTargetException()</code></li>
     * <li><code>getException()</code></li>
     * <li><code>getSourceException()</code></li>
     * <li><code>getRootCause()</code></li>
     * <li><code>getCausedByException()</code></li>
     * <li><code>getNested()</code></li>
     * </ul>
     * 
     * <p>
     * In the absence of any such method, the object is inspected for a
     * <code>detail</code> field assignable to a <code>Throwable</code>.
     * </p>
     * 
     * <p>
     * If none of the above is found, returns <code>null</code>.
     * </p>
     * 
     * @param throwable
     *            the throwable to introspect for a cause, may be null
     * @return the cause of the <code>Throwable</code>, <code>null</code> if
     *         none found or null throwable input
     * @since 1.0
     * @deprecated This feature will be removed in Lang 4.0
     */
    @Deprecated
    public static Throwable getCause(Throwable throwable) {
        return getCause(throwable, CAUSE_METHOD_NAMES);
    }

    /**
     * <p>
     * Introspects the <code>Throwable</code> to obtain the cause.
     * </p>
     * 
     * <ol>
     * <li>Try known exception types.</li>
     * <li>Try the supplied array of method names.</li>
     * <li>Try the field 'detail'.</li>
     * </ol>
     * 
     * <p>
     * A <code>null</code> set of method names means use the default set. A
     * <code>null</code> in the set of method names will be ignored.
     * </p>
     * 
     * @param throwable
     *            the throwable to introspect for a cause, may be null
     * @param methodNames
     *            the method names, null treated as default set
     * @return the cause of the <code>Throwable</code>, <code>null</code> if
     *         none found or null throwable input
     * @since 1.0
     * @deprecated This feature will be removed in Lang 4.0
     */
    @Deprecated
    public static Throwable getCause(Throwable throwable, String[] methodNames) {
        if (throwable == null) {
            return null;
        }

        if (methodNames == null) {
            methodNames = CAUSE_METHOD_NAMES;
        }

        for (int i = 0; i < methodNames.length; i++) {
            String methodName = methodNames[i];
            if (methodName != null) {
                Throwable cause = getCauseUsingMethodName(throwable, methodName);
                if (cause != null) {
                    return cause;
                }
            }
        }

        return null;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Returns the (zero based) index of the first <code>Throwable</code> that
     * matches the specified class (exactly) in the exception chain. Subclasses
     * of the specified class do not match - see
     * {@link #indexOfType(Throwable, Class)} for the opposite.
     * </p>
     * 
     * <p>
     * A <code>null</code> throwable returns <code>-1</code>. A
     * <code>null</code> type returns <code>-1</code>. No match in the chain
     * returns <code>-1</code>.
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @param clazz
     *            the class to search for, subclasses do not match, null returns
     *            -1
     * @return the index into the throwable chain, -1 if no match or null input
     */
    public static int indexOfThrowable(Throwable throwable, Class<?> clazz) {
        return indexOf(throwable, clazz, 0, false);
    }

    /**
     * <p>
     * Returns the (zero based) index of the first <code>Throwable</code> that
     * matches the specified type in the exception chain from a specified index.
     * Subclasses of the specified class do not match - see
     * {@link #indexOfType(Throwable, Class, int)} for the opposite.
     * </p>
     * 
     * <p>
     * A <code>null</code> throwable returns <code>-1</code>. A
     * <code>null</code> type returns <code>-1</code>. No match in the chain
     * returns <code>-1</code>. A negative start index is treated as zero. A
     * start index greater than the number of throwables returns <code>-1</code>
     * .
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @param clazz
     *            the class to search for, subclasses do not match, null returns
     *            -1
     * @param fromIndex
     *            the (zero based) index of the starting position, negative
     *            treated as zero, larger than chain size returns -1
     * @return the index into the throwable chain, -1 if no match or null input
     */
    public static int indexOfThrowable(Throwable throwable, Class<?> clazz,
            int fromIndex) {
        return indexOf(throwable, clazz, fromIndex, false);
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Returns the (zero based) index of the first <code>Throwable</code> that
     * matches the specified class or subclass in the exception chain.
     * Subclasses of the specified class do match - see
     * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
     * </p>
     * 
     * <p>
     * A <code>null</code> throwable returns <code>-1</code>. A
     * <code>null</code> type returns <code>-1</code>. No match in the chain
     * returns <code>-1</code>.
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @param type
     *            the type to search for, subclasses match, null returns -1
     * @return the index into the throwable chain, -1 if no match or null input
     * @since 2.1
     */
    public static int indexOfType(Throwable throwable, Class<?> type) {
        return indexOf(throwable, type, 0, true);
    }

    /**
     * <p>
     * Returns the (zero based) index of the first <code>Throwable</code> that
     * matches the specified type in the exception chain from a specified index.
     * Subclasses of the specified class do match - see
     * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
     * </p>
     * 
     * <p>
     * A <code>null</code> throwable returns <code>-1</code>. A
     * <code>null</code> type returns <code>-1</code>. No match in the chain
     * returns <code>-1</code>. A negative start index is treated as zero. A
     * start index greater than the number of throwables returns <code>-1</code>
     * .
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @param type
     *            the type to search for, subclasses match, null returns -1
     * @param fromIndex
     *            the (zero based) index of the starting position, negative
     *            treated as zero, larger than chain size returns -1
     * @return the index into the throwable chain, -1 if no match or null input
     * @since 2.1
     */
    public static int indexOfType(Throwable throwable, Class<?> type,
            int fromIndex) {
        return indexOf(throwable, type, fromIndex, true);
    }

    /**
     * <p>
     * Worker method for the <code>indexOfType</code> methods.
     * </p>
     * 
     * @param throwable
     *            the throwable to inspect, may be null
     * @param type
     *            the type to search for, subclasses match, null returns -1
     * @param fromIndex
     *            the (zero based) index of the starting position, negative
     *            treated as zero, larger than chain size returns -1
     * @param subclass
     *            if <code>true</code>, compares with
     *            {@link Class#isAssignableFrom(Class)}, otherwise compares
     *            using references
     * @return index of the <code>type</code> within throwables nested withing
     *         the specified <code>throwable</code>
     */
    private static int indexOf(Throwable throwable, Class<?> type,
            int fromIndex, boolean subclass) {
        if (throwable == null || type == null) {
            return -1;
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        Throwable[] throwables = getThrowables(throwable);
        if (fromIndex >= throwables.length) {
            return -1;
        }
        if (subclass) {
            for (int i = fromIndex; i < throwables.length; i++) {
                if (type.isAssignableFrom(throwables[i].getClass())) {
                    return i;
                }
            }
        } else {
            for (int i = fromIndex; i < throwables.length; i++) {
                if (type.equals(throwables[i].getClass())) {
                    return i;
                }
            }
        }
        return -1;
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Prints a compact stack trace for the root cause of a throwable to
     * <code>System.err</code>.
     * </p>
     * 
     * <p>
     * The compact stack trace starts with the root cause and prints stack
     * frames up to the place where it was caught and wrapped. Then it prints
     * the wrapped exception and continues with stack frames until the wrapper
     * exception is caught and wrapped again, etc.
     * </p>
     * 
     * <p>
     * The output of this method is consistent across JDK versions. Note that
     * this is the opposite order to the JDK1.4 display.
     * </p>
     * 
     * <p>
     * The method is equivalent to <code>printStackTrace</code> for throwables
     * that don't have nested causes.
     * </p>
     * 
     * @param throwable
     *            the throwable to output
     * @since 2.0
     */
    public static void printRootCauseStackTrace(Throwable throwable) {
        printRootCauseStackTrace(throwable, System.err);
    }

    /**
     * <p>
     * Prints a compact stack trace for the root cause of a throwable.
     * </p>
     * 
     * <p>
     * The compact stack trace starts with the root cause and prints stack
     * frames up to the place where it was caught and wrapped. Then it prints
     * the wrapped exception and continues with stack frames until the wrapper
     * exception is caught and wrapped again, etc.
     * </p>
     * 
     * <p>
     * The output of this method is consistent across JDK versions. Note that
     * this is the opposite order to the JDK1.4 display.
     * </p>
     * 
     * <p>
     * The method is equivalent to <code>printStackTrace</code> for throwables
     * that don't have nested causes.
     * </p>
     * 
     * @param throwable
     *            the throwable to output, may be null
     * @param stream
     *            the stream to output to, may not be null
     * @throws IllegalArgumentException
     *             if the stream is <code>null</code>
     * @since 2.0
     */
    public static void printRootCauseStackTrace(Throwable throwable,
            PrintStream stream) {
        if (throwable == null) {
            return;
        }
        if (stream == null) {
            throw new IllegalArgumentException(
                    "The PrintStream must not be null");
        }
        String trace[] = getRootCauseStackTrace(throwable);
        for (int i = 0; i < trace.length; i++) {
            stream.println(trace[i]);
        }
        stream.flush();
    }

    /**
     * <p>
     * Prints a compact stack trace for the root cause of a throwable.
     * </p>
     * 
     * <p>
     * The compact stack trace starts with the root cause and prints stack
     * frames up to the place where it was caught and wrapped. Then it prints
     * the wrapped exception and continues with stack frames until the wrapper
     * exception is caught and wrapped again, etc.
     * </p>
     * 
     * <p>
     * The output of this method is consistent across JDK versions. Note that
     * this is the opposite order to the JDK1.4 display.
     * </p>
     * 
     * <p>
     * The method is equivalent to <code>printStackTrace</code> for throwables
     * that don't have nested causes.
     * </p>
     * 
     * @param throwable
     *            the throwable to output, may be null
     * @param writer
     *            the writer to output to, may not be null
     * @throws IllegalArgumentException
     *             if the writer is <code>null</code>
     * @since 2.0
     */
    public static void printRootCauseStackTrace(Throwable throwable,
            PrintWriter writer) {
        if (throwable == null) {
            return;
        }
        if (writer == null) {
            throw new IllegalArgumentException(
                    "The PrintWriter must not be null");
        }
        String trace[] = getRootCauseStackTrace(throwable);
        for (int i = 0; i < trace.length; i++) {
            writer.println(trace[i]);
        }
        writer.flush();
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Creates a compact stack trace for the root cause of the supplied
     * <code>Throwable</code>.
     * </p>
     * 
     * <p>
     * The output of this method is consistent across JDK versions. It consists
     * of the root exception followed by each of its wrapping exceptions
     * separated by '[wrapped]'. Note that this is the opposite order to the
     * JDK1.4 display.
     * </p>
     * 
     * @param throwable
     *            the throwable to examine, may be null
     * @return an array of stack trace frames, never null
     * @since 2.0
     */
    public static String[] getRootCauseStackTrace(Throwable throwable) {
        if (throwable == null) {
            return EMPTY_STRING_ARRAY;
        }
        Throwable throwables[] = getThrowables(throwable);
        int count = throwables.length;
        List<String> frames = new ArrayList<String>();
        List<String> nextTrace = getStackFrameList(throwables[count - 1]);
        for (int i = count; --i >= 0;) {
            List<String> trace = nextTrace;
            if (i != 0) {
                nextTrace = getStackFrameList(throwables[i - 1]);
                removeCommonFrames(trace, nextTrace);
            }
            if (i == count - 1) {
                frames.add(throwables[i].toString());
            } else {
                frames.add(WRAPPED_MARKER + throwables[i].toString());
            }
            for (int j = 0; j < trace.size(); j++) {
                frames.add(trace.get(j));
            }
        }
        return frames.toArray(new String[0]);
    }

    /**
     * <p>
     * Removes common frames from the cause trace given the two stack traces.
     * </p>
     * 
     * @param causeFrames
     *            stack trace of a cause throwable
     * @param wrapperFrames
     *            stack trace of a wrapper throwable
     * @throws IllegalArgumentException
     *             if either argument is null
     * @since 2.0
     */
    public static void removeCommonFrames(List<String> causeFrames,
            List<String> wrapperFrames) {
        if (causeFrames == null || wrapperFrames == null) {
            throw new IllegalArgumentException("The List must not be null");
        }
        int causeFrameIndex = causeFrames.size() - 1;
        int wrapperFrameIndex = wrapperFrames.size() - 1;
        while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
            // Remove the frame from the cause trace if it is the same
            // as in the wrapper trace
            String causeFrame = causeFrames.get(causeFrameIndex);
            String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
            if (causeFrame.equals(wrapperFrame)) {
                causeFrames.remove(causeFrameIndex);
            }
            causeFrameIndex--;
            wrapperFrameIndex--;
        }
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Gets the stack trace from a Throwable as a String.
     * </p>
     * 
     * <p>
     * The result of this method vary by JDK version as this method uses
     * {@link Throwable#printStackTrace(java.io.PrintWriter)}. On JDK1.3 and
     * earlier, the cause exception will not be shown unless the specified
     * throwable alters printStackTrace.
     * </p>
     * 
     * @param throwable
     *            the <code>Throwable</code> to be examined
     * @return the stack trace as generated by the exception's
     *         <code>printStackTrace(PrintWriter)</code> method
     */
    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        throwable.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

    /**
     * <p>
     * Captures the stack trace associated with the specified
     * <code>Throwable</code> object, decomposing it into a list of stack
     * frames.
     * </p>
     * 
     * <p>
     * The result of this method vary by JDK version as this method uses
     * {@link Throwable#printStackTrace(java.io.PrintWriter)}. On JDK1.3 and
     * earlier, the cause exception will not be shown unless the specified
     * throwable alters printStackTrace.
     * </p>
     * 
     * @param throwable
     *            the <code>Throwable</code> to examine, may be null
     * @return an array of strings describing each stack frame, never null
     */
    public static String[] getStackFrames(Throwable throwable) {
        if (throwable == null) {
            return EMPTY_STRING_ARRAY;
        }
        return getStackFrames(getStackTrace(throwable));
    }

    // -----------------------------------------------------------------------
    /**
     * <p>
     * Returns an array where each element is a line from the argument.
     * </p>
     * 
     * <p>
     * The end of line is determined by the value of
     * {@link SystemUtils#LINE_SEPARATOR}.
     * </p>
     * 
     * @param stackTrace
     *            a stack trace String
     * @return an array where each element is a line from the argument
     */
    static String[] getStackFrames(String stackTrace) {
        String linebreak = LINE_SEPARATOR;
        StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
        List<String> list = new ArrayList<String>();
        while (frames.hasMoreTokens()) {
            list.add(frames.nextToken());
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * <p>
     * Produces a <code>List</code> of stack frames - the message is not
     * included. Only the trace of the specified exception is returned, any
     * caused by trace is stripped.
     * </p>
     * 
     * <p>
     * This works in most cases - it will only fail if the exception message
     * contains a line that starts with:
     * <code>&quot;&nbsp;&nbsp;&nbsp;at&quot;.</code>
     * </p>
     * 
     * @param t
     *            is any throwable
     * @return List of stack frames
     */
    static List<String> getStackFrameList(Throwable t) {
        String stackTrace = getStackTrace(t);
        String linebreak = LINE_SEPARATOR;
        StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
        List<String> list = new ArrayList<String>();
        boolean traceStarted = false;
        while (frames.hasMoreTokens()) {
            String token = frames.nextToken();
            // Determine if the line starts with <whitespace>at
            int at = token.indexOf("at");
            if (at != -1 && token.substring(0, at).trim().length() == 0) {
                traceStarted = true;
                list.add(token);
            } else if (traceStarted) {
                break;
            }
        }
        return list;
    }

    // -----------------------------------------------------------------------
    /**
     * Gets a short message summarising the exception.
     * <p>
     * The message returned is of the form {ClassNameWithoutPackage}:
     * {ThrowableMessage}
     * 
     * @param th
     *            the throwable to get a message for, null returns empty string
     * @return the message, non-null
     * @since Commons Lang 2.2
     */
    public static String getMessage(Throwable th) {
        if (th == null) {
            return "";
        }
        String clsName = getShortClassName(th, null);
        String msg = th.getMessage();
        return clsName + ": " + defaultString(msg);
    }

    // -----------------------------------------------------------------------
    /**
     * Gets a short message summarising the root cause exception.
     * <p>
     * The message returned is of the form {ClassNameWithoutPackage}:
     * {ThrowableMessage}
     * 
     * @param th
     *            the throwable to get a message for, null returns empty string
     * @return the message, non-null
     * @since Commons Lang 2.2
     */
    public static String getRootCauseMessage(Throwable th) {
        Throwable root = getRootCause(th);
        root = (root == null ? th : root);
        return getMessage(root);
    }

    // ///////////////////////////////////////////
    //
    // FilenameUtils
    //
    // ///////////////////////////////////////////

    /**
     * The extension separator character.
     * 
     * @since Commons IO 1.4
     */
    public static final char EXTENSION_SEPARATOR = '.';

    /**
     * The extension separator String.
     * 
     * @since Commons IO 1.4
     */
    public static final String EXTENSION_SEPARATOR_STR = Character
            .toString(EXTENSION_SEPARATOR);

    /**
     * The Unix separator character.
     */
    private static final char UNIX_SEPARATOR = '/';

    /**
     * The Windows separator character.
     */
    private static final char WINDOWS_SEPARATOR = '\\';

    /**
     * The system separator character.
     */
    private static final char SYSTEM_SEPARATOR = File.separatorChar;

    /**
     * The separator character that is the opposite of the system separator.
     */
    private static final char OTHER_SEPARATOR;

    static {
        if (isSystemWindows()) {
            OTHER_SEPARATOR = UNIX_SEPARATOR;
        } else {
            OTHER_SEPARATOR = WINDOWS_SEPARATOR;
        }
    }

    /**
     * Determines if Windows file system is in use.
     * 
     * @return true if the system is Windows
     */
    public static boolean isSystemWindows() {
        return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR;
    }

    // -----------------------------------------------------------------------
    /**
     * Checks if the character is a separator.
     * 
     * @param ch
     *            the character to check
     * @return true if it is a separator character
     */
    private static boolean isPathSeparator(char ch) {
        return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);
    }

    // -----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps.
     * <p>
     * This method normalizes a path to a standard format. The input may contain
     * separators in either Unix or Windows format. The output will contain
     * separators in the format of the system.
     * <p>
     * A trailing slash will be retained. A double slash will be merged to a
     * single slash (but UNC names are handled). A single dot path segment will
     * be removed. A double dot will cause that path segment and the one before
     * to be removed. If the double dot has no parent path segment to work with,
     * <code>null</code> is returned.
     * <p>
     * The output will be the same on both Unix and Windows except for the
     * separator character.
     * 
     * <pre>
     * /foo//               -->   /foo/
     * /foo/./              -->   /foo/
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar/
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo/
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar/
     * ~/../bar             -->   null
     * </pre>
     * 
     * (Note the file separator returned will be correct for Windows/Unix)
     * 
     * @param filename
     *            the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalizePath(String filename) {
        return doNormalizePath(filename, SYSTEM_SEPARATOR, true);
    }

    /**
     * Normalizes a path, removing double and single dot path steps.
     * <p>
     * This method normalizes a path to a standard format. The input may contain
     * separators in either Unix or Windows format. The output will contain
     * separators in the format specified.
     * <p>
     * A trailing slash will be retained. A double slash will be merged to a
     * single slash (but UNC names are handled). A single dot path segment will
     * be removed. A double dot will cause that path segment and the one before
     * to be removed. If the double dot has no parent path segment to work with,
     * <code>null</code> is returned.
     * <p>
     * The output will be the same on both Unix and Windows except for the
     * separator character.
     * 
     * <pre>
     * /foo//               -->   /foo/
     * /foo/./              -->   /foo/
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar/
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo/
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar/
     * ~/../bar             -->   null
     * </pre>
     * 
     * The output will be the same on both Unix and Windows including the
     * separator character.
     * 
     * @param filename
     *            the filename to normalize, null returns null
     * @param unixSeparator
     *            <code>true</code> if a unix separator should be used or
     *            <code>false</code> if a windows separator should be used.
     * @return the normalized filename, or null if invalid
     * @since Commons IO 2.0
     */
    public static String normalizePath(String filename, boolean unixSeparator) {
        char separator = (unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR);
        return doNormalizePath(filename, separator, true);
    }

    // -----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps, and
     * removing any final directory separator.
     * <p>
     * This method normalizes a path to a standard format. The input may contain
     * separators in either Unix or Windows format. The output will contain
     * separators in the format of the system.
     * <p>
     * A trailing slash will be removed. A double slash will be merged to a
     * single slash (but UNC names are handled). A single dot path segment will
     * be removed. A double dot will cause that path segment and the one before
     * to be removed. If the double dot has no parent path segment to work with,
     * <code>null</code> is returned.
     * <p>
     * The output will be the same on both Unix and Windows except for the
     * separator character.
     * 
     * <pre>
     * /foo//               -->   /foo
     * /foo/./              -->   /foo
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar
     * ~/../bar             -->   null
     * </pre>
     * 
     * (Note the file separator returned will be correct for Windows/Unix)
     * 
     * @param filename
     *            the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalizeNoEndSeparator(String filename) {
        return doNormalizePath(filename, SYSTEM_SEPARATOR, false);
    }

    /**
     * Normalizes a path, removing double and single dot path steps, and
     * removing any final directory separator.
     * <p>
     * This method normalizes a path to a standard format. The input may contain
     * separators in either Unix or Windows format. The output will contain
     * separators in the format specified.
     * <p>
     * A trailing slash will be removed. A double slash will be merged to a
     * single slash (but UNC names are handled). A single dot path segment will
     * be removed. A double dot will cause that path segment and the one before
     * to be removed. If the double dot has no parent path segment to work with,
     * <code>null</code> is returned.
     * <p>
     * The output will be the same on both Unix and Windows including the
     * separator character.
     * 
     * <pre>
     * /foo//               -->   /foo
     * /foo/./              -->   /foo
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar
     * ~/../bar             -->   null
     * </pre>
     * 
     * @param filename
     *            the filename to normalize, null returns null
     * @param unixSeparator
     *            <code>true</code> if a unix separator should be used or
     *            <code>false</code> if a windows separtor should be used.
     * @return the normalized filename, or null if invalid
     * @since Commons IO 2.0
     */
    public static String normalizeNoEndSeparator(String filename,
            boolean unixSeparator) {
        char separator = (unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR);
        return doNormalizePath(filename, separator, false);
    }

    /**
     * Internal method to perform the normalization.
     * 
     * @param filename
     *            the filename
     * @param separator
     *            The separator character to use
     * @param keepSeparator
     *            true to keep the final separator
     * @return the normalized filename
     */
    private static String doNormalizePath(String filename, char separator,
            boolean keepSeparator) {
        if (filename == null) {
            return null;
        }
        int size = filename.length();
        if (size == 0) {
            return filename;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }

        char[] array = new char[size + 2]; // +1 for possible extra slash, +2
        // for arraycopy
        filename.getChars(0, filename.length(), array, 0);

        // fix separators throughout
        char otherSeparator = (separator == SYSTEM_SEPARATOR ? OTHER_SEPARATOR
                : SYSTEM_SEPARATOR);
        for (int i = 0; i < array.length; i++) {
            if (array[i] == otherSeparator) {
                array[i] = separator;
            }
        }

        // add extra separator on the end to simplify code below
        boolean lastIsDirectory = true;
        if (array[size - 1] != separator) {
            array[size++] = separator;
            lastIsDirectory = false;
        }

        // adjoining slashes
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == separator && array[i - 1] == separator) {
                System.arraycopy(array, i, array, i - 1, size - i);
                size--;
                i--;
            }
        }

        // dot slash
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == separator && array[i - 1] == '.'
                    && (i == prefix + 1 || array[i - 2] == separator)) {
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                System.arraycopy(array, i + 1, array, i - 1, size - i);
                size -= 2;
                i--;
            }
        }

        // double dot slash
        outer: for (int i = prefix + 2; i < size; i++) {
            if (array[i] == separator && array[i - 1] == '.'
                    && array[i - 2] == '.'
                    && (i == prefix + 2 || array[i - 3] == separator)) {
                if (i == prefix + 2) {
                    return null;
                }
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                int j;
                for (j = i - 4; j >= prefix; j--) {
                    if (array[j] == separator) {
                        // remove b/../ from a/b/../c
                        System.arraycopy(array, i + 1, array, j + 1, size - i);
                        size -= (i - j);
                        i = j + 1;
                        continue outer;
                    }
                }
                // remove a/../ from a/../c
                System.arraycopy(array, i + 1, array, prefix, size - i);
                size -= (i + 1 - prefix);
                i = prefix + 1;
            }
        }

        if (size <= 0) { // should never be less than 0
            return "";
        }
        if (size <= prefix) { // should never be less than prefix
            return new String(array, 0, size);
        }
        if (lastIsDirectory && keepSeparator) {
            return new String(array, 0, size); // keep trailing separator
        }
        return new String(array, 0, size - 1); // lose trailing separator
    }

    // -----------------------------------------------------------------------
    /**
     * Concatenates a filename to a base path using normal command line style
     * rules.
     * <p>
     * The effect is equivalent to resultant directory after changing directory
     * to the first argument, followed by changing directory to the second
     * argument.
     * <p>
     * The first argument is the base path, the second is the path to
     * concatenate. The returned path is always normalized via
     * {@link #normalizePath(String)}, thus <code>..</code> is handled.
     * <p>
     * If <code>pathToAdd</code> is absolute (has an absolute prefix), then it
     * will be normalized and returned. Otherwise, the paths will be joined,
     * normalized and returned.
     * <p>
     * The output will be the same on both Unix and Windows except for the
     * separator character.
     * 
     * <pre>
     * /foo/ + bar          -->   /foo/bar
     * /foo + bar           -->   /foo/bar
     * /foo + /bar          -->   /bar
     * /foo + C:/bar        -->   C:/bar
     * /foo + C:bar         -->   C:bar (*)
     * /foo/a/ + ../bar     -->   foo/bar
     * /foo/ + ../../bar    -->   null
     * /foo/ + /bar         -->   /bar
     * /foo/.. + /bar       -->   /bar
     * /foo + bar/c.txt     -->   /foo/bar/c.txt
     * /foo/c.txt + bar     -->   /foo/c.txt/bar (!)
     * </pre>
     * 
     * (*) Note that the Windows relative drive prefix is unreliable when used
     * with this method. (!) Note that the first parameter must be a path. If it
     * ends with a name, then the name will be built into the concatenated path.
     * If this might be a problem, use {@link #getFullPath(String)} on the base
     * path argument.
     * 
     * @param basePath
     *            the base path to attach to, always treated as a path
     * @param fullFilenameToAdd
     *            the filename (or path) to attach to the base
     * @return the concatenated path, or null if invalid
     */
    public static String concatPath(String basePath, String fullFilenameToAdd) {
        int prefix = getPrefixLength(fullFilenameToAdd);
        if (prefix < 0) {
            return null;
        }
        if (prefix > 0) {
            return normalizePath(fullFilenameToAdd);
        }
        if (basePath == null) {
            return null;
        }
        int len = basePath.length();
        if (len == 0) {
            return normalizePath(fullFilenameToAdd);
        }
        char ch = basePath.charAt(len - 1);
        if (isPathSeparator(ch)) {
            return normalizePath(basePath + fullFilenameToAdd);
        } else {
            return normalizePath(basePath + '/' + fullFilenameToAdd);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Converts all separators to the Unix separator of forward slash.
     * 
     * @param path
     *            the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToUnix(String path) {
        if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);
    }

    /**
     * Converts all separators to the Windows separator of backslash.
     * 
     * @param path
     *            the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToWindows(String path) {
        if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);
    }

    /**
     * Converts all separators to the system separator.
     * 
     * @param path
     *            the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToSystem(String path) {
        if (path == null) {
            return null;
        }
        if (isSystemWindows()) {
            return separatorsToWindows(path);
        } else {
            return separatorsToUnix(path);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Returns the length of the filename prefix, such as <code>C:/</code> or
     * <code>~/</code>.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * <p>
     * The prefix length includes the first slash in the full filename if
     * applicable. Thus, it is possible that the length returned is greater than
     * the length of the input string.
     * 
     * <pre>
     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     * 
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on. ie. both Unix and Windows prefixes are matched regardless.
     * 
     * @param filename
     *            the filename to find the prefix in, null returns -1
     * @return the length of the prefix, -1 if invalid or null
     */
    public static int getPrefixLength(String filename) {
        if (filename == null) {
            return -1;
        }
        int len = filename.length();
        if (len == 0) {
            return 0;
        }
        char ch0 = filename.charAt(0);
        if (ch0 == ':') {
            return -1;
        }
        if (len == 1) {
            if (ch0 == '~') {
                return 2; // return a length greater than the input
            }
            return (isPathSeparator(ch0) ? 1 : 0);
        } else {
            if (ch0 == '~') {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);
                if (posUnix == -1 && posWin == -1) {
                    return len + 1; // return a length greater than the input
                }
                posUnix = (posUnix == -1 ? posWin : posUnix);
                posWin = (posWin == -1 ? posUnix : posWin);
                return Math.min(posUnix, posWin) + 1;
            }
            char ch1 = filename.charAt(1);
            if (ch1 == ':') {
                ch0 = Character.toUpperCase(ch0);
                if (ch0 >= 'A' && ch0 <= 'Z') {
                    if (len == 2
                            || isPathSeparator(filename.charAt(2)) == false) {
                        return 2;
                    }
                    return 3;
                }
                return -1;

            } else if (isPathSeparator(ch0) && isPathSeparator(ch1)) {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
                if ((posUnix == -1 && posWin == -1) || posUnix == 2
                        || posWin == 2) {
                    return -1;
                }
                posUnix = (posUnix == -1 ? posWin : posUnix);
                posWin = (posWin == -1 ? posUnix : posWin);
                return Math.min(posUnix, posWin) + 1;
            } else {
                return (isPathSeparator(ch0) ? 1 : 0);
            }
        }
    }

    /**
     * Returns the index of the last directory separator character.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * position of the last forward or backslash is returned.
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to find the last path separator in, null returns
     *            -1
     * @return the index of the last separator character, or -1 if there is no
     *         such character
     */
    public static int indexOfLastSeparator(String filename) {
        if (filename == null) {
            return -1;
        }
        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
        return Math.max(lastUnixPos, lastWindowsPos);
    }

    /**
     * Returns the index of the last extension separator character, which is a
     * dot.
     * <p>
     * This method also checks that there is no directory separator after the
     * last dot. To do this it uses {@link #indexOfLastSeparator(String)} which
     * will handle a file in either Unix or Windows format.
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to find the last path separator in, null returns
     *            -1
     * @return the index of the last separator character, or -1 if there is no
     *         such character
     */
    public static int indexOfExtension(String filename) {
        if (filename == null) {
            return -1;
        }
        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
        int lastSeparator = indexOfLastSeparator(filename);
        return (lastSeparator > extensionPos ? -1 : extensionPos);
    }

    // -----------------------------------------------------------------------
    /**
     * Gets the prefix from a full filename, such as <code>C:/</code> or
     * <code>~/</code>.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * prefix includes the first slash in the full filename where applicable.
     * 
     * <pre>
     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     * 
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on. ie. both Unix and Windows prefixes are matched regardless.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the prefix of the file, null if invalid
     */
    public static String getPathPrefix(String filename) {
        if (filename == null) {
            return null;
        }
        int len = getPrefixLength(filename);
        if (len < 0) {
            return null;
        }
        if (len > filename.length()) {
            return filename + UNIX_SEPARATOR; // we know this only happens for
            // unix
        }
        return filename.substring(0, len);
    }

    /**
     * Gets the path from a full filename, which excludes the prefix.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * method is entirely text based, and returns the text before and including
     * the last forward or backslash.
     * 
     * <pre>
     * C:\a\b\c.txt --> a\b\
     * ~/a/b/c.txt  --> a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * <p>
     * This method drops the prefix from the result. See
     * {@link #getFullPath(String)} for the method that retains the prefix.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if
     *         invalid
     */
    public static String getPath(String filename) {
        return doGetPath(filename, 1);
    }

    /**
     * Gets the path from a full filename, which excludes the prefix, and also
     * excluding the final directory separator.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * method is entirely text based, and returns the text before the last
     * forward or backslash.
     * 
     * <pre>
     * C:\a\b\c.txt --> a\b
     * ~/a/b/c.txt  --> a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * <p>
     * This method drops the prefix from the result. See
     * {@link #getFullPathNoEndSeparator(String)} for the method that retains
     * the prefix.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if
     *         invalid
     */
    public static String getPathNoEndSeparator(String filename) {
        return doGetPath(filename, 0);
    }

    /**
     * Does the work of getting the path.
     * 
     * @param filename
     *            the filename
     * @param separatorAdd
     *            0 to omit the end separator, 1 to return it
     * @return the path
     */
    private static String doGetPath(String filename, int separatorAdd) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        int endIndex = index + separatorAdd;
        if (prefix >= filename.length() || index < 0 || prefix >= endIndex) {
            return "";
        }
        return filename.substring(prefix, endIndex);
    }

    /**
     * Gets the full path from a full filename, which is the prefix + path.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * method is entirely text based, and returns the text before and including
     * the last forward or backslash.
     * 
     * <pre>
     * C:\a\b\c.txt --> C:\a\b\
     * ~/a/b/c.txt  --> ~/a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~/
     * ~/           --> ~/
     * ~user        --> ~user/
     * ~user/       --> ~user/
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if
     *         invalid
     */
    public static String getFullPath(String filename) {
        return doGetFullPath(filename, true);
    }

    /**
     * Gets the full path from a full filename, which is the prefix + path, and
     * also excluding the final directory separator.
     * <p>
     * This method will handle a file in either Unix or Windows format. The
     * method is entirely text based, and returns the text before the last
     * forward or backslash.
     * 
     * <pre>
     * C:\a\b\c.txt --> C:\a\b
     * ~/a/b/c.txt  --> ~/a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~
     * ~/           --> ~
     * ~user        --> ~user
     * ~user/       --> ~user
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if
     *         invalid
     */
    public static String getFullPathNoEndSeparator(String filename) {
        return doGetFullPath(filename, false);
    }

    /**
     * Does the work of getting the path.
     * 
     * @param filename
     *            the filename
     * @param includeSeparator
     *            true to include the end separator
     * @return the path
     */
    private static String doGetFullPath(String filename,
            boolean includeSeparator) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        if (prefix >= filename.length()) {
            if (includeSeparator) {
                return getPathPrefix(filename); // add end slash if necessary
            } else {
                return filename;
            }
        }
        int index = indexOfLastSeparator(filename);
        if (index < 0) {
            return filename.substring(0, prefix);
        }
        int end = index + (includeSeparator ? 1 : 0);
        if (end == 0) {
            end++;
        }
        return filename.substring(0, end);
    }

    /**
     * Gets the name minus the path from a full filename.
     * <p>
     * This method will handle a file in either Unix or Windows format. The text
     * after the last forward or backslash is returned.
     * 
     * <pre>
     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * a/b/c     --> c
     * a/b/c/    --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none
     *         exists
     */
    public static String getFileName(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        return filename.substring(index + 1);
    }

    /**
     * Gets the base name, minus the full path and extension, from a full
     * filename.
     * <p>
     * This method will handle a file in either Unix or Windows format. The text
     * after the last forward or backslash and before the last dot is returned.
     * 
     * <pre>
     * a/b/c.txt --> c
     * a.txt     --> a
     * a/b/c     --> c
     * a/b/c/    --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none
     *         exists
     */
    public static String getBaseName(String filename) {
        return removeExtension(getFileName(filename));
    }

    /**
     * Gets the extension of a filename.
     * <p>
     * This method returns the textual part of the filename after the last dot.
     * There must be no directory separator after the dot.
     * 
     * <pre>
     * foo.txt      --> "txt"
     * a/b/c.jpg    --> "jpg"
     * a/b.txt/c    --> ""
     * a/b/c        --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to retrieve the extension of.
     * @return the extension of the file or an empty string if none exists or
     *         <code>null</code> if the filename is <code>null</code>.
     */
    public static String getExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return "";
        } else {
            return filename.substring(index + 1);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Removes the extension from a filename.
     * <p>
     * This method returns the textual part of the filename before the last dot.
     * There must be no directory separator after the dot.
     * 
     * <pre>
     * foo.txt    --> foo
     * a\b\c.jpg  --> a\b\c
     * a\b\c      --> a\b\c
     * a.b\c      --> a.b\c
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is
     * running on.
     * 
     * @param filename
     *            the filename to query, null returns null
     * @return the filename minus the extension
     */
    public static String removeExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return filename;
        } else {
            return filename.substring(0, index);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal exactly.
     * <p>
     * No processing is performed on the filenames other than comparison, thus
     * this is merely a null-safe case-sensitive equals.
     * 
     * @param filename1
     *            the first filename to query, may be null
     * @param filename2
     *            the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equalsFilenames(String filename1, String filename2) {
        return equalsFilenames(filename1, filename2, false, IOCase.SENSITIVE);
    }

    /**
     * Checks whether two filenames are equal using the case rules of the
     * system.
     * <p>
     * No processing is performed on the filenames other than comparison. The
     * check is case-sensitive on Unix and case-insensitive on Windows.
     * 
     * @param filename1
     *            the first filename to query, may be null
     * @param filename2
     *            the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsOnSystem(String filename1, String filename2) {
        return equalsFilenames(filename1, filename2, false, IOCase.SYSTEM);
    }

    // -----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal after both have been normalized.
     * <p>
     * Both filenames are first passed to {@link #normalizePath(String)}. The
     * check is then performed in a case-sensitive manner.
     * 
     * @param filename1
     *            the first filename to query, may be null
     * @param filename2
     *            the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equalsNormalized(String filename1, String filename2) {
        return equalsFilenames(filename1, filename2, true, IOCase.SENSITIVE);
    }

    /**
     * Checks whether two filenames are equal after both have been normalized
     * and using the case rules of the system.
     * <p>
     * Both filenames are first passed to {@link #normalizePath(String)}. The
     * check is then performed case-sensitive on Unix and case-insensitive on
     * Windows.
     * 
     * @param filename1
     *            the first filename to query, may be null
     * @param filename2
     *            the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsNormalizedOnSystem(String filename1,
            String filename2) {
        return equalsFilenames(filename1, filename2, true, IOCase.SYSTEM);
    }

    /**
     * Checks whether two filenames are equal, optionally normalizing and
     * providing control over the case-sensitivity.
     * 
     * @param filename1
     *            the first filename to query, may be null
     * @param filename2
     *            the second filename to query, may be null
     * @param normalized
     *            whether to normalize the filenames
     * @param caseSensitivity
     *            what case sensitivity rule to use, null means case-sensitive
     * @return true if the filenames are equal, null equals null
     * @since Commons IO 1.3
     */
    public static boolean equalsFilenames(String filename1, String filename2,
            boolean normalized, IOCase caseSensitivity) {

        if (filename1 == null || filename2 == null) {
            return (filename1 == null && filename2 == null);
        }
        if (normalized) {
            filename1 = normalizePath(filename1);
            filename2 = normalizePath(filename2);
            if (filename1 == null || filename2 == null) {
                throw new NullPointerException(
                        "Error normalizing one or both of the file names");
            }
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        return caseSensitivity.checkEquals(filename1, filename2);
    }

    // -----------------------------------------------------------------------
    /**
     * Checks whether the extension of the filename is that specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     * 
     * @param filename
     *            the filename to query, null returns false
     * @param extension
     *            the extension to check for, null or empty checks for no
     *            extension
     * @return true if the filename has the specified extension
     */
    public static boolean isExtension(String filename, String extension) {
        if (filename == null) {
            return false;
        }
        if (extension == null || extension.length() == 0) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        return fileExt.equals(extension);
    }

    /**
     * Checks whether the extension of the filename is one of those specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     * 
     * @param filename
     *            the filename to query, null returns false
     * @param extensions
     *            the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename, String[] extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.length == 0) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        for (String extension : extensions) {
            if (fileExt.equals(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks whether the extension of the filename is one of those specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     * 
     * @param filename
     *            the filename to query, null returns false
     * @param extensions
     *            the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename,
            Collection<String> extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.isEmpty()) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        for (String extension : extensions) {
            if (fileExt.equals(extension)) {
                return true;
            }
        }
        return false;
    }

    // -----------------------------------------------------------------------
    /**
     * Checks a filename to see if it matches the specified wildcard matcher,
     * always testing case-sensitive.
     * <p>
     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters. This is the same
     * as often found on Dos/Unix command lines. The check is case-sensitive
     * always.
     * 
     * <pre>
     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     * </pre>
     * 
     * N.B. the sequence "*?" does not work properly at present in match
     * strings.
     * 
     * @param filename
     *            the filename to match on
     * @param wildcardMatcher
     *            the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SENSITIVE
     */
    public static boolean wildcardMatch(String filename, String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
    }

    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * using the case rules of the system.
     * <p>
     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters. This is the same
     * as often found on Dos/Unix command lines. The check is case-sensitive on
     * Unix and case-insensitive on Windows.
     * 
     * <pre>
     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     * </pre>
     * 
     * N.B. the sequence "*?" does not work properly at present in match
     * strings.
     * 
     * @param filename
     *            the filename to match on
     * @param wildcardMatcher
     *            the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SYSTEM
     */
    public static boolean wildcardMatchOnSystem(String filename,
            String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
    }

    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * allowing control over case-sensitivity.
     * <p>
     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters. N.B. the sequence
     * "*?" does not work properly at present in match strings.
     * 
     * @param filename
     *            the filename to match on
     * @param wildcardMatcher
     *            the wildcard string to match against
     * @param caseSensitivity
     *            what case sensitivity rule to use, null means case-sensitive
     * @return true if the filename matches the wilcard string
     * @since Commons IO 1.3
     */
    public static boolean wildcardMatch(String filename,
            String wildcardMatcher, IOCase caseSensitivity) {
        if (filename == null && wildcardMatcher == null) {
            return true;
        }
        if (filename == null || wildcardMatcher == null) {
            return false;
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        String[] wcs = splitOnTokens(wildcardMatcher);
        boolean anyChars = false;
        int textIdx = 0;
        int wcsIdx = 0;
        Stack<int[]> backtrack = new Stack<int[]>();

        // loop around a backtrack stack, to handle complex * matching
        do {
            if (backtrack.size() > 0) {
                int[] array = backtrack.pop();
                wcsIdx = array[0];
                textIdx = array[1];
                anyChars = true;
            }

            // loop whilst tokens and text left to process
            while (wcsIdx < wcs.length) {

                if (wcs[wcsIdx].equals("?")) {
                    // ? so move to next text char
                    textIdx++;
                    if (textIdx > filename.length()) {
                        break;
                    }
                    anyChars = false;

                } else if (wcs[wcsIdx].equals("*")) {
                    // set any chars status
                    anyChars = true;
                    if (wcsIdx == wcs.length - 1) {
                        textIdx = filename.length();
                    }

                } else {
                    // matching text token
                    if (anyChars) {
                        // any chars then try to locate text token
                        textIdx = caseSensitivity.checkIndexOf(filename,
                                textIdx, wcs[wcsIdx]);
                        if (textIdx == -1) {
                            // token not found
                            break;
                        }
                        int repeat = caseSensitivity.checkIndexOf(filename,
                                textIdx + 1, wcs[wcsIdx]);
                        if (repeat >= 0) {
                            backtrack.push(new int[] { wcsIdx, repeat });
                        }
                    } else {
                        // matching from current position
                        if (!caseSensitivity.checkRegionMatches(filename,
                                textIdx, wcs[wcsIdx])) {
                            // couldnt match token
                            break;
                        }
                    }

                    // matched text token, move text index to end of matched
                    // token
                    textIdx += wcs[wcsIdx].length();
                    anyChars = false;
                }

                wcsIdx++;
            }

            // full match
            if (wcsIdx == wcs.length && textIdx == filename.length()) {
                return true;
            }

        } while (backtrack.size() > 0);

        return false;
    }

    /**
     * Splits a string into a number of tokens. The text is split by '?' and
     * '*'. Where multiple '*' occur consecutively they are collapsed into a
     * single '*'.
     * 
     * @param text
     *            the text to split
     * @return the array of tokens, never null
     */
    static String[] splitOnTokens(String text) {
        // used by wildcardMatch
        // package level so a unit test may run on this

        if (text.indexOf('?') == -1 && text.indexOf('*') == -1) {
            return new String[] { text };
        }

        char[] array = text.toCharArray();
        ArrayList<String> list = new ArrayList<String>();
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            if (array[i] == '?' || array[i] == '*') {
                if (buffer.length() != 0) {
                    list.add(buffer.toString());
                    buffer.setLength(0);
                }
                if (array[i] == '?') {
                    list.add("?");
                } else if (list.size() == 0
                        || (i > 0 && list.get(list.size() - 1).equals("*") == false)) {
                    list.add("*");
                }
            } else {
                buffer.append(array[i]);
            }
        }
        if (buffer.length() != 0) {
            list.add(buffer.toString());
        }

        return list.toArray(new String[list.size()]);
    }

    // ///////////////////////////////////////////
    //
    // FileFilterUtils
    //
    // ///////////////////////////////////////////

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting array is a subset of the original file list that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link Set} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * Set&lt;File&gt; allFiles = ...
     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
     *     FileFilterUtils.suffixFileFilter(".java"));
     * </pre>
     * 
     * @param filter
     *            the filter to apply to the set of files.
     * @param files
     *            the array of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * 
     * @since Commons IO 2.0
     */
    public static File[] fileFilter(IOFileFilter filter, File... files) {
        if (filter == null) {
            throw new IllegalArgumentException("file filter is null");
        }
        if (files == null) {
            return new File[0];
        }
        List<File> acceptedFiles = new ArrayList<File>();
        for (File file : files) {
            if (file == null) {
                throw new IllegalArgumentException("file array contains null");
            }
            if (filter.accept(file)) {
                acceptedFiles.add(file);
            }
        }
        return acceptedFiles.toArray(new File[acceptedFiles.size()]);
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting array is a subset of the original file list that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link Set} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * Set&lt;File&gt; allFiles = ...
     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
     *     FileFilterUtils.suffixFileFilter(".java"));
     * </pre>
     * 
     * @param filter
     *            the filter to apply to the set of files.
     * @param files
     *            the array of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * 
     * @since Commons IO 2.0
     */
    public static File[] filter(IOFileFilter filter, Iterable<File> files) {
        List<File> acceptedFiles = filterList(filter, files);
        return acceptedFiles.toArray(new File[acceptedFiles.size()]);
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting list is a subset of the original files that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link List} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * List&lt;File&gt; filesAndDirectories = ...
     * List&lt;File&gt; directories = FileFilterUtils.filterList(filesAndDirectories,
     *     FileFilterUtils.directoryFileFilter());
     * </pre>
     * 
     * @param filter
     *            the filter to apply to each files in the list.
     * @param files
     *            the collection of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * @since Commons IO 2.0
     */
    public static List<File> filterList(IOFileFilter filter,
            Iterable<File> files) {
        return filter(filter, files, new ArrayList<File>());
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting list is a subset of the original files that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link List} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * List&lt;File&gt; filesAndDirectories = ...
     * List&lt;File&gt; directories = FileFilterUtils.filterList(filesAndDirectories,
     *     FileFilterUtils.directoryFileFilter());
     * </pre>
     * 
     * @param filter
     *            the filter to apply to each files in the list.
     * @param files
     *            the collection of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * @since Commons IO 2.0
     */
    public static List<File> filterList(IOFileFilter filter, File... files) {
        File[] acceptedFiles = fileFilter(filter, files);
        return Arrays.asList(acceptedFiles);
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting set is a subset of the original file list that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link Set} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * Set&lt;File&gt; allFiles = ...
     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
     *     FileFilterUtils.suffixFileFilter(".java"));
     * </pre>
     * 
     * @param filter
     *            the filter to apply to the set of files.
     * @param files
     *            the collection of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * 
     * @since Commons IO 2.0
     */
    public static Set<File> filterSet(IOFileFilter filter, File... files) {
        File[] acceptedFiles = fileFilter(filter, files);
        return new HashSet<File>(Arrays.asList(acceptedFiles));
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects. The
     * resulting set is a subset of the original file list that matches the
     * provided filter.
     * </p>
     * 
     * <p>
     * The {@link Set} returned by this method is not guaranteed to be thread
     * safe.
     * </p>
     * 
     * <pre>
     * Set&lt;File&gt; allFiles = ...
     * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
     *     FileFilterUtils.suffixFileFilter(".java"));
     * </pre>
     * 
     * @param filter
     *            the filter to apply to the set of files.
     * @param files
     *            the collection of files to apply the filter to.
     * 
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     * 
     * @since Commons IO 2.0
     */
    public static Set<File> filterSet(IOFileFilter filter, Iterable<File> files) {
        return filter(filter, files, new HashSet<File>());
    }

    /**
     * <p>
     * Applies an {@link IOFileFilter} to the provided {@link File} objects and
     * appends the accepted files to the other supplied collection.
     * </p>
     * 
     * <pre>
     * List&lt;File&gt; files = ...
     * List&lt;File&gt; directories = FileFilterUtils.filterList(files,
     *     FileFilterUtils.sizeFileFilter(FileUtils.FIFTY_MB), 
     *         new ArrayList&lt;File&gt;());
     * </pre>
     * 
     * @param filter
     *            the filter to apply to the collection of files.
     * @param files
     *            the collection of files to apply the filter to.
     * @param acceptedFiles
     *            the list of files to add accepted files to.
     * 
     * @param <T>
     *            the type of the file collection.
     * @return a subset of <code>files</code> that is accepted by the file
     *         filter.
     * @throws IllegalArgumentException
     *             if the filter is <code>null</code> or <code>files</code>
     *             contains a <code>null</code> value.
     */
    private static <T extends Collection<File>> T filter(IOFileFilter filter,
            Iterable<File> files, T acceptedFiles) {
        if (filter == null) {
            throw new IllegalArgumentException("file filter is null");
        }
        if (files != null) {
            for (File file : files) {
                if (file == null) {
                    throw new IllegalArgumentException(
                            "file collection contains null");
                }
                if (filter.accept(file)) {
                    acceptedFiles.add(file);
                }
            }
        }
        return acceptedFiles;
    }

    /**
     * Returns a filter that returns true if the filename starts with the
     * specified text.
     * 
     * @param prefix
     *            the filename prefix
     * @return a prefix checking filter
     * @see PrefixFileFilter
     */
    public static IOFileFilter prefixFileFilter(String prefix) {
        return new PrefixFileFilter(prefix);
    }

    /**
     * Returns a filter that returns true if the filename starts with the
     * specified text.
     * 
     * @param prefix
     *            the filename prefix
     * @param caseSensitivity
     *            how to handle case sensitivity, null means case-sensitive
     * @return a prefix checking filter
     * @see PrefixFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter prefixFileFilter(String prefix,
            IOCase caseSensitivity) {
        return new PrefixFileFilter(prefix, caseSensitivity);
    }

    /**
     * Returns a filter that returns true if the filename ends with the
     * specified text.
     * 
     * @param suffix
     *            the filename suffix
     * @return a suffix checking filter
     * @see SuffixFileFilter
     */
    public static IOFileFilter suffixFileFilter(String suffix) {
        return new SuffixFileFilter(suffix);
    }

    /**
     * Returns a filter that returns true if the filename ends with the
     * specified text.
     * 
     * @param suffix
     *            the filename suffix
     * @param caseSensitivity
     *            how to handle case sensitivity, null means case-sensitive
     * @return a suffix checking filter
     * @see SuffixFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter suffixFileFilter(String suffix,
            IOCase caseSensitivity) {
        return new SuffixFileFilter(suffix, caseSensitivity);
    }

    /**
     * Returns a filter that returns true if the filename matches the specified
     * text.
     * 
     * @param name
     *            the filename
     * @return a name checking filter
     * @see NameFileFilter
     */
    public static IOFileFilter nameFileFilter(String name) {
        return new NameFileFilter(name);
    }

    /**
     * Returns a filter that returns true if the filename matches the specified
     * text.
     * 
     * @param name
     *            the filename
     * @param caseSensitivity
     *            how to handle case sensitivity, null means case-sensitive
     * @return a name checking filter
     * @see NameFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter nameFileFilter(String name,
            IOCase caseSensitivity) {
        return new NameFileFilter(name, caseSensitivity);
    }

    /**
     * Returns a filter that checks if the file is a directory.
     * 
     * @return file filter that accepts only directories and not files
     * @see DirectoryFileFilter#DIRECTORY
     */
    public static IOFileFilter directoryFileFilter() {
        return DirectoryFileFilter.DIRECTORY;
    }

    /**
     * Returns a filter that checks if the file is a file (and not a directory).
     * 
     * @return file filter that accepts only files and not directories
     * @see FileFileFilter#FILE
     */
    public static IOFileFilter fileFileFilter() {
        return FileFileFilter.FILE;
    }

    /**
     * Returns a filter that ANDs the specified filters.
     * 
     * @param filters
     *            the IOFileFilters that will be ANDed together.
     * @return a filter that ANDs the specified filters
     * 
     * @throws IllegalArgumentException
     *             if the filters are null or contain a null value.
     * @see AndFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter andFileFilter(IOFileFilter... filters) {
        return new AndFileFilter(toIOFileFilterList(filters));
    }

    /**
     * Returns a filter that ORs the specified filters.
     * 
     * @param filters
     *            the IOFileFilters that will be ORed together.
     * @return a filter that ORs the specified filters
     * 
     * @throws IllegalArgumentException
     *             if the filters are null or contain a null value.
     * @see OrFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter orFileFilter(IOFileFilter... filters) {
        return new OrFileFilter(toIOFileFilterList(filters));
    }

    /**
     * Create a List of file filters.
     * 
     * @param filters
     *            The file filters
     * @return The list of file filters
     * @throws IllegalArgumentException
     *             if the filters are null or contain a null value.
     * @since Commons IO 2.0
     */
    public static List<IOFileFilter> toIOFileFilterList(IOFileFilter... filters) {
        if (filters == null) {
            throw new IllegalArgumentException("The filters must not be null");
        }
        List<IOFileFilter> list = new ArrayList<IOFileFilter>(filters.length);
        for (int i = 0; i < filters.length; i++) {
            if (filters[i] == null) {
                throw new IllegalArgumentException("The filter[" + i
                        + "] is null");
            }
            list.add(filters[i]);
        }
        return list;
    }

    /**
     * Returns a filter that NOTs the specified filter.
     * 
     * @param filter
     *            the filter to invert
     * @return a filter that NOTs the specified filter
     * @see NotFileFilter
     */
    public static IOFileFilter notFileFilter(IOFileFilter filter) {
        return new NotFileFilter(filter);
    }

    // -----------------------------------------------------------------------
    /**
     * Returns a filter that always returns true.
     * 
     * @return a true filter
     * @see TrueFileFilter#TRUE
     */
    public static IOFileFilter trueFileFilter() {
        return TrueFileFilter.TRUE;
    }

    /**
     * Returns a filter that always returns false.
     * 
     * @return a false filter
     * @see FalseFileFilter#FALSE
     */
    public static IOFileFilter falseFileFilter() {
        return FalseFileFilter.FALSE;
    }

    // -----------------------------------------------------------------------
    /**
     * Returns an <code>IOFileFilter</code> that wraps the
     * <code>FileFilter</code> instance.
     * 
     * @param filter
     *            the filter to be wrapped
     * @return a new filter that implements IOFileFilter
     * @see DelegateFileFilter
     */
    public static IOFileFilter asFileFilter(FileFilter filter) {
        return new DelegateFileFilter(filter);
    }

    /**
     * Returns an <code>IOFileFilter</code> that wraps the
     * <code>FilenameFilter</code> instance.
     * 
     * @param filter
     *            the filter to be wrapped
     * @return a new filter that implements IOFileFilter
     * @see DelegateFileFilter
     */
    public static IOFileFilter asFileFilter(FilenameFilter filter) {
        return new DelegateFileFilter(filter);
    }

    // -----------------------------------------------------------------------
    /**
     * Returns a filter that returns true if the file was last modified after
     * the specified cutoff time.
     * 
     * @param cutoff
     *            the time threshold
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(long cutoff) {
        return new AgeFileFilter(cutoff);
    }

    /**
     * Returns a filter that filters files based on a cutoff time.
     * 
     * @param cutoff
     *            the time threshold
     * @param acceptOlder
     *            if true, older files get accepted, if false, newer
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(long cutoff, boolean acceptOlder) {
        return new AgeFileFilter(cutoff, acceptOlder);
    }

    /**
     * Returns a filter that returns true if the file was last modified after
     * the specified cutoff date.
     * 
     * @param cutoffDate
     *            the time threshold
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(Date cutoffDate) {
        return new AgeFileFilter(cutoffDate);
    }

    /**
     * Returns a filter that filters files based on a cutoff date.
     * 
     * @param cutoffDate
     *            the time threshold
     * @param acceptOlder
     *            if true, older files get accepted, if false, newer
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(Date cutoffDate,
            boolean acceptOlder) {
        return new AgeFileFilter(cutoffDate, acceptOlder);
    }

    /**
     * Returns a filter that returns true if the file was last modified after
     * the specified reference file.
     * 
     * @param cutoffReference
     *            the file whose last modification time is usesd as the
     *            threshold age of the files
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(File cutoffReference) {
        return new AgeFileFilter(cutoffReference);
    }

    /**
     * Returns a filter that filters files based on a cutoff reference file.
     * 
     * @param cutoffReference
     *            the file whose last modification time is usesd as the
     *            threshold age of the files
     * @param acceptOlder
     *            if true, older files get accepted, if false, newer
     * @return an appropriately configured age file filter
     * @see AgeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter ageFileFilter(File cutoffReference,
            boolean acceptOlder) {
        return new AgeFileFilter(cutoffReference, acceptOlder);
    }

    // -----------------------------------------------------------------------
    /**
     * Returns a filter that returns true if the file is bigger than a certain
     * size.
     * 
     * @param threshold
     *            the file size threshold
     * @return an appropriately configured SizeFileFilter
     * @see SizeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter sizeFileFilter(long threshold) {
        return new SizeFileFilter(threshold);
    }

    /**
     * Returns a filter that filters based on file size.
     * 
     * @param threshold
     *            the file size threshold
     * @param acceptLarger
     *            if true, larger files get accepted, if false, smaller
     * @return an appropriately configured SizeFileFilter
     * @see SizeFileFilter
     * @since Commons IO 1.2
     */
    public static IOFileFilter sizeFileFilter(long threshold,
            boolean acceptLarger) {
        return new SizeFileFilter(threshold, acceptLarger);
    }

    /**
     * Returns a filter that accepts files whose size is &gt;= minimum size and
     * &lt;= maximum size.
     * 
     * @param minSizeInclusive
     *            the minimum file size (inclusive)
     * @param maxSizeInclusive
     *            the maximum file size (inclusive)
     * @return an appropriately configured IOFileFilter
     * @see SizeFileFilter
     * @since Commons IO 1.3
     */
    public static IOFileFilter sizeRangeFileFilter(long minSizeInclusive,
            long maxSizeInclusive) {
        IOFileFilter minimumFilter = new SizeFileFilter(minSizeInclusive, true);
        IOFileFilter maximumFilter = new SizeFileFilter(maxSizeInclusive + 1L,
                false);
        return new AndFileFilter(minimumFilter, maximumFilter);
    }

    /**
     * Returns a filter that accepts files that begin with the provided magic
     * number.
     * 
     * @param magicNumber
     *            the magic number (byte sequence) to match at the beginning of
     *            each file.
     * 
     * @return an IOFileFilter that accepts files beginning with the provided
     *         magic number.
     * 
     * @throws IllegalArgumentException
     *             if <code>magicNumber</code> is <code>null</code> or the empty
     *             String.
     * @see MagicNumberFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter magicNumberFileFilter(String magicNumber) {
        return new MagicNumberFileFilter(magicNumber);
    }

    /**
     * Returns a filter that accepts files that contains the provided magic
     * number at a specified offset within the file.
     * 
     * @param magicNumber
     *            the magic number (byte sequence) to match at the provided
     *            offset in each file.
     * @param offset
     *            the offset within the files to look for the magic number.
     * 
     * @return an IOFileFilter that accepts files containing the magic number at
     *         the specified offset.
     * 
     * @throws IllegalArgumentException
     *             if <code>magicNumber</code> is <code>null</code> or the empty
     *             String, or if offset is a negative number.
     * @see MagicNumberFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter magicNumberFileFilter(String magicNumber,
            long offset) {
        return new MagicNumberFileFilter(magicNumber, offset);
    }

    /**
     * Returns a filter that accepts files that begin with the provided magic
     * number.
     * 
     * @param magicNumber
     *            the magic number (byte sequence) to match at the beginning of
     *            each file.
     * 
     * @return an IOFileFilter that accepts files beginning with the provided
     *         magic number.
     * 
     * @throws IllegalArgumentException
     *             if <code>magicNumber</code> is <code>null</code> or is of
     *             length zero.
     * @see MagicNumberFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter magicNumberFileFilter(byte[] magicNumber) {
        return new MagicNumberFileFilter(magicNumber);
    }

    /**
     * Returns a filter that accepts files that contains the provided magic
     * number at a specified offset within the file.
     * 
     * @param magicNumber
     *            the magic number (byte sequence) to match at the provided
     *            offset in each file.
     * @param offset
     *            the offset within the files to look for the magic number.
     * 
     * @return an IOFileFilter that accepts files containing the magic number at
     *         the specified offset.
     * 
     * @throws IllegalArgumentException
     *             if <code>magicNumber</code> is <code>null</code>, or contains
     *             no bytes, or <code>offset</code> is a negative number.
     * @see MagicNumberFileFilter
     * @since Commons IO 2.0
     */
    public static IOFileFilter magicNumberFileFilter(byte[] magicNumber,
            long offset) {
        return new MagicNumberFileFilter(magicNumber, offset);
    }

    // -----------------------------------------------------------------------
    /* Constructed on demand and then cached */
    private static final IOFileFilter cvsFilter = notFileFilter(andFileFilter(
            directoryFileFilter(), nameFileFilter("CVS")));

    /* Constructed on demand and then cached */
    private static final IOFileFilter svnFilter = notFileFilter(andFileFilter(
            directoryFileFilter(), nameFileFilter(".svn")));

    /**
     * Decorates a filter to make it ignore CVS directories. Passing in
     * <code>null</code> will return a filter that accepts everything except CVS
     * directories.
     * 
     * @param filter
     *            the filter to decorate, null means an unrestricted filter
     * @return the decorated filter, never null
     * @since Commons IO 1.1 (method existed but had bug in 1.0)
     */
    public static IOFileFilter makeCVSAware(IOFileFilter filter) {
        if (filter == null) {
            return cvsFilter;
        } else {
            return andFileFilter(filter, cvsFilter);
        }
    }

    /**
     * Decorates a filter to make it ignore SVN directories. Passing in
     * <code>null</code> will return a filter that accepts everything except SVN
     * directories.
     * 
     * @param filter
     *            the filter to decorate, null means an unrestricted filter
     * @return the decorated filter, never null
     * @since Commons IO 1.1
     */
    public static IOFileFilter makeSVNAware(IOFileFilter filter) {
        if (filter == null) {
            return svnFilter;
        } else {
            return andFileFilter(filter, svnFilter);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Decorates a filter so that it only applies to directories and not to
     * files.
     * 
     * @param filter
     *            the filter to decorate, null means an unrestricted filter
     * @return the decorated filter, never null
     * @see DirectoryFileFilter#DIRECTORY
     * @since Commons IO 1.3
     */
    public static IOFileFilter makeDirectoryOnly(IOFileFilter filter) {
        if (filter == null) {
            return DirectoryFileFilter.DIRECTORY;
        }
        return new AndFileFilter(DirectoryFileFilter.DIRECTORY, filter);
    }

    /**
     * Decorates a filter so that it only applies to files and not to
     * directories.
     * 
     * @param filter
     *            the filter to decorate, null means an unrestricted filter
     * @return the decorated filter, never null
     * @see FileFileFilter#FILE
     * @since Commons IO 1.3
     */
    public static IOFileFilter makeFileOnly(IOFileFilter filter) {
        if (filter == null) {
            return FileFileFilter.FILE;
        }
        return new AndFileFilter(FileFileFilter.FILE, filter);
    }

    // ///////////////////////////////////////////
    //
    // FileUtils
    //
    // ///////////////////////////////////////////

    /**
     * The number of bytes in a kilobyte.
     */
    public static final long ONE_KB = 1024;

    /**
     * The number of bytes in a megabyte.
     */
    public static final long ONE_MB = ONE_KB * ONE_KB;

    /**
     * The number of bytes in a 50 MB.
     */
    private static final long FIFTY_MB = ONE_MB * 50;

    /**
     * The number of bytes in a gigabyte.
     */
    public static final long ONE_GB = ONE_KB * ONE_MB;

    /**
     * An empty array of type <code>File</code>.
     */
    public static final File[] EMPTY_FILE_ARRAY = new File[0];

    /**
     * The UTF-8 character set, used to decode octets in URLs.
     */
    private static final Charset UTF8 = Charset.forName("UTF-8");

    // -----------------------------------------------------------------------
    /**
     * Returns the path to the system temporary directory.
     * 
     * @return the path to the system temporary directory.
     * 
     * @since Commons IO 2.0
     */
    public static String getTempDirectoryPath() {
        return System.getProperty("java.io.tmpdir");
    }

    /**
     * Returns a {@link File} representing the system temporary directory.
     * 
     * @return the system temporary directory.
     * 
     * @since Commons IO 2.0
     */
    public static File getTempDirectory() {
        return new File(getTempDirectoryPath());
    }

    /**
     * Returns the path to the user's home directory.
     * 
     * @return the path to the user's home directory.
     * 
     * @since Commons IO 2.0
     */
    public static String getUserDirectoryPath() {
        return System.getProperty("user.home");
    }

    /**
     * Returns a {@link File} representing the user's home directory.
     * 
     * @return the user's home directory.
     * 
     * @since Commons IO 2.0
     */
    public static File getUserDirectory() {
        return new File(getUserDirectoryPath());
    }

    // -----------------------------------------------------------------------
    /**
     * Opens a {@link FileInputStream} for the specified file, providing better
     * error messages than simply calling <code>new FileInputStream(file)</code>
     * .
     * <p>
     * At the end of the method either the stream will be successfully opened,
     * or an exception will have been thrown.
     * <p>
     * An exception is thrown if the file does not exist. An exception is thrown
     * if the file object exists but is a directory. An exception is thrown if
     * the file exists but cannot be read.
     * 
     * @param file
     *            the file to open for input, must not be <code>null</code>
     * @return a new {@link FileInputStream} for the specified file
     * @throws FileNotFoundException
     *             if the file does not exist
     * @throws IOException
     *             if the file object is a directory
     * @throws IOException
     *             if the file cannot be read
     * @since Commons IO 1.3
     */
    public static FileInputStream openInputStream(File file) throws IOException {
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("File '" + file
                        + "' exists but is a directory");
            }
            if (file.canRead() == false) {
                throw new IOException("File '" + file + "' cannot be read");
            }
        } else {
            throw new FileNotFoundException("File '" + file
                    + "' does not exist");
        }
        return new FileInputStream(file);
    }

    // -----------------------------------------------------------------------
    /**
     * Opens a {@link FileOutputStream} for the specified file, checking and
     * creating the parent directory if it does not exist.
     * <p>
     * At the end of the method either the stream will be successfully opened,
     * or an exception will have been thrown.
     * <p>
     * The parent directory will be created if it does not exist. The file will
     * be created if it does not exist. An exception is thrown if the file
     * object exists but is a directory. An exception is thrown if the file
     * exists but cannot be written to. An exception is thrown if the parent
     * directory cannot be created.
     * 
     * @param file
     *            the file to open for output, must not be <code>null</code>
     * @return a new {@link FileOutputStream} for the specified file
     * @throws IOException
     *             if the file object is a directory
     * @throws IOException
     *             if the file cannot be written to
     * @throws IOException
     *             if a parent directory needs creating but that fails
     * @since Commons IO 1.3
     */
    public static FileOutputStream openOutputStream(File file)
            throws IOException {
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("File '" + file
                        + "' exists but is a directory");
            }
            if (file.canWrite() == false) {
                throw new IOException("File '" + file
                        + "' cannot be written to");
            }
        } else {
            File parent = file.getParentFile();
            if (parent != null && parent.exists() == false) {
                if (parent.mkdirs() == false) {
                    throw new IOException("File '" + file
                            + "' could not be created");
                }
            }
        }
        return new FileOutputStream(file);
    }

    // -----------------------------------------------------------------------
    /**
     * Returns a human-readable version of the file size, where the input
     * represents a specific number of bytes.
     * 
     * If the size is over 1GB, the size is returned as the number of whole GB,
     * i.e. the size is rounded down to the nearest GB boundary.
     * 
     * Similarly for the 1MB and 1KB boundaries.
     * 
     * @param size
     *            the number of bytes
     * @return a human-readable display value (includes units - GB, MB, KB or
     *         bytes)
     */
    // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be
    // changed?
    public static String byteCountToDisplaySize(long size) {
        String displaySize;

        if (size / ONE_GB > 0) {
            displaySize = String.valueOf(size / ONE_GB) + " GB";
        } else if (size / ONE_MB > 0) {
            displaySize = String.valueOf(size / ONE_MB) + " MB";
        } else if (size / ONE_KB > 0) {
            displaySize = String.valueOf(size / ONE_KB) + " KB";
        } else {
            displaySize = String.valueOf(size) + " bytes";
        }
        return displaySize;
    }

    // -----------------------------------------------------------------------
    /**
     * Implements the same behaviour as the "touch" utility on Unix. It creates
     * a new file with size 0 or, if the file exists already, it is opened and
     * closed without modifying it, but updating the file date and time.
     * <p>
     * NOTE: As from v1.3, this method throws an IOException if the last
     * modified date of the file cannot be set. Also, as from v1.3 this method
     * creates parent directories if they do not exist.
     * 
     * @param file
     *            the File to touch
     * @throws IOException
     *             If an I/O problem occurs
     */
    public static void touch(File file) throws IOException {
        if (!file.exists()) {
            OutputStream out = openOutputStream(file);
            closeQuietly(out);
        }
        boolean success = file.setLastModified(System.currentTimeMillis());
        if (!success) {
            throw new IOException(
                    "Unable to set the last modification time for " + file);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Converts a Collection containing java.io.File instanced into array
     * representation. This is to account for the difference between
     * File.listFiles() and FileUtils.listFiles().
     * 
     * @param files
     *            a Collection containing java.io.File instances
     * @return an array of java.io.File
     */
    public static File[] convertFileCollectionToFileArray(Collection<File> files) {
        return files.toArray(new File[files.size()]);
    }

    // -----------------------------------------------------------------------
    /**
     * Finds files within a given directory (and optionally its subdirectories).
     * All files found are filtered by an IOFileFilter.
     * 
     * @param files
     *            the collection of files found.
     * @param directory
     *            the directory to search in.
     * @param filter
     *            the filter to apply to files and directories.
     */
    private static void innerListFiles(Collection<File> files, File directory,
            IOFileFilter filter) {
        File[] found = directory.listFiles((FileFilter) filter);
        if (found != null) {
            for (File file : found) {
                if (file.isDirectory()) {
                    innerListFiles(files, file, filter);
                } else {
                    files.add(file);
                }
            }
        }
    }

    /**
     * Finds files within a given directory (and optionally its subdirectories).
     * All files found are filtered by an IOFileFilter.
     * <p>
     * If your search should recurse into subdirectories you can pass in an
     * IOFileFilter for directories. You don't need to bind a
     * DirectoryFileFilter (via logical AND) to this filter. This method does
     * that for you.
     * <p>
     * An example: If you want to search through all directories called "temp"
     * you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
     * <p>
     * Another common usage of this method is find files in a directory tree but
     * ignoring the directories generated CVS. You can simply pass in
     * <code>FileFilterUtils.makeCVSAware(null)</code>.
     * 
     * @param directory
     *            the directory to search in
     * @param fileFilter
     *            filter to apply when finding files.
     * @param dirFilter
     *            optional filter to apply when finding subdirectories. If this
     *            parameter is <code>null</code>, subdirectories will not be
     *            included in the search. Use TrueFileFilter.INSTANCE to match
     *            all directories.
     * @return an collection of java.io.File with the matching files
     * @see NameFileFilter
     */
    public static Collection<File> listFiles(File directory,
            IOFileFilter fileFilter, IOFileFilter dirFilter) {
        if (!directory.isDirectory()) {
            throw new IllegalArgumentException(
                    "Parameter 'directory' is not a directory");
        }
        if (fileFilter == null) {
            throw new NullPointerException("Parameter 'fileFilter' is null");
        }

        // Setup effective file filter
        IOFileFilter effFileFilter = andFileFilter(fileFilter,
                notFileFilter(DirectoryFileFilter.INSTANCE));

        // Setup effective directory filter
        IOFileFilter effDirFilter;
        if (dirFilter == null) {
            effDirFilter = FalseFileFilter.INSTANCE;
        } else {
            effDirFilter = andFileFilter(dirFilter,
                    DirectoryFileFilter.INSTANCE);
        }

        // Find files
        Collection<File> files = new java.util.LinkedList<File>();
        innerListFiles(files, directory,
                orFileFilter(effFileFilter, effDirFilter));
        return files;
    }

    /**
     * Allows iteration over the files in given directory (and optionally its
     * subdirectories).
     * <p>
     * All files found are filtered by an IOFileFilter. This method is based on
     * {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
     * 
     * @param directory
     *            the directory to search in
     * @param fileFilter
     *            filter to apply when finding files.
     * @param dirFilter
     *            optional filter to apply when finding subdirectories. If this
     *            parameter is <code>null</code>, subdirectories will not be
     *            included in the search. Use TrueFileFilter.INSTANCE to match
     *            all directories.
     * @return an iterator of java.io.File for the matching files
     * @see NameFileFilter
     * @since Commons IO 1.2
     */
    public static Iterator<File> iterateFiles(File directory,
            IOFileFilter fileFilter, IOFileFilter dirFilter) {
        return listFiles(directory, fileFilter, dirFilter).iterator();
    }

    // -----------------------------------------------------------------------
    /**
     * Converts an array of file extensions to suffixes for use with
     * IOFileFilters.
     * 
     * @param extensions
     *            an array of extensions. Format: {"java", "xml"}
     * @return an array of suffixes. Format: {".java", ".xml"}
     */
    private static String[] toSuffixes(String[] extensions) {
        String[] suffixes = new String[extensions.length];
        for (int i = 0; i < extensions.length; i++) {
            suffixes[i] = "." + extensions[i];
        }
        return suffixes;
    }

    /**
     * Finds files within a given directory (and optionally its subdirectories)
     * which match an array of extensions.
     * 
     * @param directory
     *            the directory to search in
     * @param extensions
     *            an array of extensions, ex. {"java","xml"}. If this parameter
     *            is <code>null</code>, all files are returned.
     * @param recursive
     *            if true all subdirectories are searched as well
     * @return an collection of java.io.File with the matching files
     */
    public static Collection<File> listFiles(File directory,
            String[] extensions, boolean recursive) {
        IOFileFilter filter;
        if (extensions == null) {
            filter = TrueFileFilter.INSTANCE;
        } else {
            String[] suffixes = toSuffixes(extensions);
            filter = new SuffixFileFilter(suffixes);
        }
        return listFiles(
                directory,
                filter,
                (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
    }

    /**
     * Allows iteration over the files in a given directory (and optionally its
     * subdirectories) which match an array of extensions. This method is based
     * on {@link #listFiles(File, String[], boolean)}.
     * 
     * @param directory
     *            the directory to search in
     * @param extensions
     *            an array of extensions, ex. {"java","xml"}. If this parameter
     *            is <code>null</code>, all files are returned.
     * @param recursive
     *            if true all subdirectories are searched as well
     * @return an iterator of java.io.File with the matching files
     * @since Commons IO 1.2
     */
    public static Iterator<File> iterateFiles(File directory,
            String[] extensions, boolean recursive) {
        return listFiles(directory, extensions, recursive).iterator();
    }

    // -----------------------------------------------------------------------
    /**
     * Compares the contents of two files to determine if they are equal or not.
     * <p>
     * This method checks to see if the two files are different lengths or if
     * they point to the same file, before resorting to byte-by-byte comparison
     * of the contents.
     * <p>
     * Code origin: Avalon
     * 
     * @param file1
     *            the first file
     * @param file2
     *            the second file
     * @return true if the content of the files are equal or they both don't
     *         exist, false otherwise
     * @throws IOException
     *             in case of an I/O error
     */
    public static boolean contentEquals(File file1, File file2)
            throws IOException {
        boolean file1Exists = file1.exists();
        if (file1Exists != file2.exists()) {
            return false;
        }

        if (!file1Exists) {
            // two not existing files are equal
            return true;
        }

        if (file1.isDirectory() || file2.isDirectory()) {
            // don't want to compare directory contents
            throw new IOException("Can't compare directories, only files");
        }

        if (file1.length() != file2.length()) {
            // lengths differ, cannot be equal
            return false;
        }

        if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
            // same file
            return true;
        }

        InputStream input1 = null;
        InputStream input2 = null;
        try {
            input1 = new FileInputStream(file1);
            input2 = new FileInputStream(file2);
            return contentEquals(input1, input2);

        } finally {
            closeQuietly(input1);
            closeQuietly(input2);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Convert from a <code>URL</code> to a <code>File</code>.
     * <p>
     * From version 1.1 this method will decode the URL. Syntax such as
     * <code>file:///my%20docs/file.txt</code> will be correctly decoded to
     * <code>/my docs/file.txt</code>. Starting with version 1.5, this method
     * uses UTF-8 to decode percent-encoded octets to characters. Additionally,
     * malformed percent-encoded octets are handled leniently by passing them
     * through literally.
     * 
     * @param url
     *            the file URL to convert, <code>null</code> returns
     *            <code>null</code>
     * @return the equivalent <code>File</code> object, or <code>null</code> if
     *         the URL's protocol is not <code>file</code>
     */
    public static File toFile(URL url) {
        if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
            return null;
        } else {
            String filename = url.getFile().replace('/', File.separatorChar);
            filename = decodeUrl(filename);
            return new File(filename);
        }
    }

    /**
     * Decodes the specified URL as per RFC 3986, i.e. transforms
     * percent-encoded octets to characters by decoding with the UTF-8 character
     * set. This function is primarily intended for usage with
     * {@link java.net.URL} which unfortunately does not enforce proper URLs. As
     * such, this method will leniently accept invalid characters or malformed
     * percent-encoded octets and simply pass them literally through to the
     * result string. Except for rare edge cases, this will make unencoded URLs
     * pass through unaltered.
     * 
     * @param url
     *            The URL to decode, may be <code>null</code>.
     * @return The decoded URL or <code>null</code> if the input was
     *         <code>null</code>.
     */
    static String decodeUrl(String url) {
        String decoded = url;
        if (url != null && url.indexOf('%') >= 0) {
            int n = url.length();
            StringBuffer buffer = new StringBuffer();
            ByteBuffer bytes = ByteBuffer.allocate(n);
            for (int i = 0; i < n;) {
                if (url.charAt(i) == '%') {
                    try {
                        do {
                            byte octet = (byte) Integer.parseInt(
                                    url.substring(i + 1, i + 3), 16);
                            bytes.put(octet);
                            i += 3;
                        } while (i < n && url.charAt(i) == '%');
                        continue;
                    } catch (RuntimeException e) {
                        // malformed percent-encoded octet, fall through and
                        // append characters literally
                    } finally {
                        if (bytes.position() > 0) {
                            bytes.flip();
                            buffer.append(UTF8.decode(bytes).toString());
                            bytes.clear();
                        }
                    }
                }
                buffer.append(url.charAt(i++));
            }
            decoded = buffer.toString();
        }
        return decoded;
    }

    /**
     * Converts each of an array of <code>URL</code> to a <code>File</code>.
     * <p>
     * Returns an array of the same size as the input. If the input is
     * <code>null</code>, an empty array is returned. If the input contains
     * <code>null</code>, the output array contains <code>null</code> at the
     * same index.
     * <p>
     * This method will decode the URL. Syntax such as
     * <code>file:///my%20docs/file.txt</code> will be correctly decoded to
     * <code>/my docs/file.txt</code>.
     * 
     * @param urls
     *            the file URLs to convert, <code>null</code> returns empty
     *            array
     * @return a non-<code>null</code> array of Files matching the input, with a
     *         <code>null</code> item if there was a <code>null</code> at that
     *         index in the input array
     * @throws IllegalArgumentException
     *             if any file is not a URL file
     * @throws IllegalArgumentException
     *             if any file is incorrectly encoded
     * @since Commons IO 1.1
     */
    public static File[] toFiles(URL[] urls) {
        if (urls == null || urls.length == 0) {
            return EMPTY_FILE_ARRAY;
        }
        File[] files = new File[urls.length];
        for (int i = 0; i < urls.length; i++) {
            URL url = urls[i];
            if (url != null) {
                if (url.getProtocol().equals("file") == false) {
                    throw new IllegalArgumentException(
                            "URL could not be converted to a File: " + url);
                }
                files[i] = toFile(url);
            }
        }
        return files;
    }

    /**
     * Converts each of an array of <code>File</code> to a <code>URL</code>.
     * <p>
     * Returns an array of the same size as the input.
     * 
     * @param files
     *            the files to convert
     * @return an array of URLs matching the input
     * @throws IOException
     *             if a file cannot be converted
     */
    public static URL[] toURLs(File[] files) throws IOException {
        URL[] urls = new URL[files.length];

        for (int i = 0; i < urls.length; i++) {
            urls[i] = files[i].toURI().toURL();
        }

        return urls;
    }

    // -----------------------------------------------------------------------
    /**
     * Copies a file to a directory preserving the file date.
     * <p>
     * This method copies the contents of the specified source file to a file of
     * the same name in the specified destination directory. The destination
     * directory is created if it does not exist. If the destination file
     * exists, then this method will overwrite it.
     * <p>
     * <strong>Note:</strong> This method tries to preserve the file's last
     * modified date/times using {@link File#setLastModified(long)}, however it
     * is not guaranteed that the operation will succeed. If the modification
     * operation fails, no indication is provided.
     * 
     * @param srcFile
     *            an existing file to copy, must not be <code>null</code>
     * @param destDir
     *            the directory to place the copy in, must not be
     *            <code>null</code>
     * 
     * @throws NullPointerException
     *             if source or destination is null
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @see #copyFile(File, File, boolean)
     */
    public static void copyFileToDirectory(File srcFile, File destDir)
            throws IOException {
        copyFileToDirectory(srcFile, destDir, true);
    }

    /**
     * Copies a file to a directory optionally preserving the file date.
     * <p>
     * This method copies the contents of the specified source file to a file of
     * the same name in the specified destination directory. The destination
     * directory is created if it does not exist. If the destination file
     * exists, then this method will overwrite it.
     * <p>
     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
     * <code>true</code> tries to preserve the file's last modified date/times
     * using {@link File#setLastModified(long)}, however it is not guaranteed
     * that the operation will succeed. If the modification operation fails, no
     * indication is provided.
     * 
     * @param srcFile
     *            an existing file to copy, must not be <code>null</code>
     * @param destDir
     *            the directory to place the copy in, must not be
     *            <code>null</code>
     * @param preserveFileDate
     *            true if the file date of the copy should be the same as the
     *            original
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @see #copyFile(File, File, boolean)
     * @since Commons IO 1.3
     */
    public static void copyFileToDirectory(File srcFile, File destDir,
            boolean preserveFileDate) throws IOException {
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (destDir.exists() && destDir.isDirectory() == false) {
            throw new IllegalArgumentException("Destination '" + destDir
                    + "' is not a directory");
        }
        File destFile = new File(destDir, srcFile.getName());
        copyFile(srcFile, destFile, preserveFileDate);
    }

    /**
     * Copies a file to a new location preserving the file date.
     * <p>
     * This method copies the contents of the specified source file to the
     * specified destination file. The directory holding the destination file is
     * created if it does not exist. If the destination file exists, then this
     * method will overwrite it.
     * <p>
     * <strong>Note:</strong> This method tries to preserve the file's last
     * modified date/times using {@link File#setLastModified(long)}, however it
     * is not guaranteed that the operation will succeed. If the modification
     * operation fails, no indication is provided.
     * 
     * @param srcFile
     *            an existing file to copy, must not be <code>null</code>
     * @param destFile
     *            the new file, must not be <code>null</code>
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @see #copyFileToDirectory(File, File)
     */
    public static void copyFile(File srcFile, File destFile) throws IOException {
        copyFile(srcFile, destFile, true);
    }

    /**
     * Copies a file to a new location.
     * <p>
     * This method copies the contents of the specified source file to the
     * specified destination file. The directory holding the destination file is
     * created if it does not exist. If the destination file exists, then this
     * method will overwrite it.
     * <p>
     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
     * <code>true</code> tries to preserve the file's last modified date/times
     * using {@link File#setLastModified(long)}, however it is not guaranteed
     * that the operation will succeed. If the modification operation fails, no
     * indication is provided.
     * 
     * @param srcFile
     *            an existing file to copy, must not be <code>null</code>
     * @param destFile
     *            the new file, must not be <code>null</code>
     * @param preserveFileDate
     *            true if the file date of the copy should be the same as the
     *            original
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @see #copyFileToDirectory(File, File, boolean)
     */
    public static void copyFile(File srcFile, File destFile,
            boolean preserveFileDate) throws IOException {
        if (srcFile == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destFile == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (srcFile.exists() == false) {
            throw new FileNotFoundException("Source '" + srcFile
                    + "' does not exist");
        }
        if (srcFile.isDirectory()) {
            throw new IOException("Source '" + srcFile
                    + "' exists but is a directory");
        }
        if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
            throw new IOException("Source '" + srcFile + "' and destination '"
                    + destFile + "' are the same");
        }
        if (destFile.getParentFile() != null
                && destFile.getParentFile().exists() == false) {
            if (destFile.getParentFile().mkdirs() == false) {
                throw new IOException("Destination '" + destFile
                        + "' directory cannot be created");
            }
        }
        if (destFile.exists() && destFile.canWrite() == false) {
            throw new IOException("Destination '" + destFile
                    + "' exists but is read-only");
        }
        doCopyFile(srcFile, destFile, preserveFileDate);
    }

    /**
     * Internal copy file method.
     * 
     * @param srcFile
     *            the validated source file, must not be <code>null</code>
     * @param destFile
     *            the validated destination file, must not be <code>null</code>
     * @param preserveFileDate
     *            whether to preserve the file date
     * @throws IOException
     *             if an error occurs
     */
    private static void doCopyFile(File srcFile, File destFile,
            boolean preserveFileDate) throws IOException {
        if (destFile.exists() && destFile.isDirectory()) {
            throw new IOException("Destination '" + destFile
                    + "' exists but is a directory");
        }

        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel input = null;
        FileChannel output = null;
        try {
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);
            input = fis.getChannel();
            output = fos.getChannel();
            long size = input.size();
            long pos = 0;
            long count = 0;
            while (pos < size) {
                count = (size - pos) > FIFTY_MB ? FIFTY_MB : (size - pos);
                pos += output.transferFrom(input, pos, count);
            }
        } finally {
            closeQuietly(output);
            closeQuietly(fos);
            closeQuietly(input);
            closeQuietly(fis);
        }

        if (srcFile.length() != destFile.length()) {
            throw new IOException("Failed to copy full contents from '"
                    + srcFile + "' to '" + destFile + "'");
        }
        if (preserveFileDate) {
            destFile.setLastModified(srcFile.lastModified());
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Copies a directory to within another directory preserving the file dates.
     * <p>
     * This method copies the source directory and all its contents to a
     * directory of the same name in the specified destination directory.
     * <p>
     * The destination directory is created if it does not exist. If the
     * destination directory did exist, then this method merges the source with
     * the destination, with the source taking precedence.
     * <p>
     * <strong>Note:</strong> This method tries to preserve the files' last
     * modified date/times using {@link File#setLastModified(long)}, however it
     * is not guaranteed that those operations will succeed. If the modification
     * operation fails, no indication is provided.
     * 
     * @param srcDir
     *            an existing directory to copy, must not be <code>null</code>
     * @param destDir
     *            the directory to place the copy in, must not be
     *            <code>null</code>
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 1.2
     */
    public static void copyDirectoryToDirectory(File srcDir, File destDir)
            throws IOException {
        if (srcDir == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (srcDir.exists() && srcDir.isDirectory() == false) {
            throw new IllegalArgumentException("Source '" + destDir
                    + "' is not a directory");
        }
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (destDir.exists() && destDir.isDirectory() == false) {
            throw new IllegalArgumentException("Destination '" + destDir
                    + "' is not a directory");
        }
        copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
    }

    /**
     * Copies a whole directory to a new location preserving the file dates.
     * <p>
     * This method copies the specified directory and all its child directories
     * and files to the specified destination. The destination is the new
     * location and name of the directory.
     * <p>
     * The destination directory is created if it does not exist. If the
     * destination directory did exist, then this method merges the source with
     * the destination, with the source taking precedence.
     * <p>
     * <strong>Note:</strong> This method tries to preserve the files' last
     * modified date/times using {@link File#setLastModified(long)}, however it
     * is not guaranteed that those operations will succeed. If the modification
     * operation fails, no indication is provided.
     * 
     * @param srcDir
     *            an existing directory to copy, must not be <code>null</code>
     * @param destDir
     *            the new directory, must not be <code>null</code>
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 1.1
     */
    public static void copyDirectory(File srcDir, File destDir)
            throws IOException {
        copyDirectory(srcDir, destDir, true);
    }

    /**
     * Copies a whole directory to a new location.
     * <p>
     * This method copies the contents of the specified source directory to
     * within the specified destination directory.
     * <p>
     * The destination directory is created if it does not exist. If the
     * destination directory did exist, then this method merges the source with
     * the destination, with the source taking precedence.
     * <p>
     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
     * <code>true</code> tries to preserve the files' last modified date/times
     * using {@link File#setLastModified(long)}, however it is not guaranteed
     * that those operations will succeed. If the modification operation fails,
     * no indication is provided.
     * 
     * @param srcDir
     *            an existing directory to copy, must not be <code>null</code>
     * @param destDir
     *            the new directory, must not be <code>null</code>
     * @param preserveFileDate
     *            true if the file date of the copy should be the same as the
     *            original
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 1.1
     */
    public static void copyDirectory(File srcDir, File destDir,
            boolean preserveFileDate) throws IOException {
        copyDirectory(srcDir, destDir, null, preserveFileDate);
    }

    /**
     * Copies a filtered directory to a new location preserving the file dates.
     * <p>
     * This method copies the contents of the specified source directory to
     * within the specified destination directory.
     * <p>
     * The destination directory is created if it does not exist. If the
     * destination directory did exist, then this method merges the source with
     * the destination, with the source taking precedence.
     * <p>
     * <strong>Note:</strong> This method tries to preserve the files' last
     * modified date/times using {@link File#setLastModified(long)}, however it
     * is not guaranteed that those operations will succeed. If the modification
     * operation fails, no indication is provided.
     * 
     * <h4>Example: Copy directories only</h4>
     * 
     * <pre>
     * // only copy the directory structure
     * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
     * </pre>
     * 
     * <h4>Example: Copy directories and txt files</h4>
     * 
     * <pre>
     * // Create a filter for &quot;.txt&quot; files
     * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(&quot;.txt&quot;);
     * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE,
     *         txtSuffixFilter);
     * 
     * // Create a filter for either directories or &quot;.txt&quot; files
     * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY,
     *         txtFiles);
     * 
     * // Copy using the filter
     * FileUtils.copyDirectory(srcDir, destDir, filter);
     * </pre>
     * 
     * @param srcDir
     *            an existing directory to copy, must not be <code>null</code>
     * @param destDir
     *            the new directory, must not be <code>null</code>
     * @param filter
     *            the filter to apply, null means copy all directories and files
     *            should be the same as the original
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 1.4
     */
    public static void copyDirectory(File srcDir, File destDir,
            FileFilter filter) throws IOException {
        copyDirectory(srcDir, destDir, filter, true);
    }

    /**
     * Copies a filtered directory to a new location.
     * <p>
     * This method copies the contents of the specified source directory to
     * within the specified destination directory.
     * <p>
     * The destination directory is created if it does not exist. If the
     * destination directory did exist, then this method merges the source with
     * the destination, with the source taking precedence.
     * <p>
     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
     * <code>true</code> tries to preserve the files' last modified date/times
     * using {@link File#setLastModified(long)}, however it is not guaranteed
     * that those operations will succeed. If the modification operation fails,
     * no indication is provided.
     * 
     * <h4>Example: Copy directories only</h4>
     * 
     * <pre>
     * // only copy the directory structure
     * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
     * </pre>
     * 
     * <h4>Example: Copy directories and txt files</h4>
     * 
     * <pre>
     * // Create a filter for &quot;.txt&quot; files
     * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(&quot;.txt&quot;);
     * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE,
     *         txtSuffixFilter);
     * 
     * // Create a filter for either directories or &quot;.txt&quot; files
     * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY,
     *         txtFiles);
     * 
     * // Copy using the filter
     * FileUtils.copyDirectory(srcDir, destDir, filter, false);
     * </pre>
     * 
     * @param srcDir
     *            an existing directory to copy, must not be <code>null</code>
     * @param destDir
     *            the new directory, must not be <code>null</code>
     * @param filter
     *            the filter to apply, null means copy all directories and files
     * @param preserveFileDate
     *            true if the file date of the copy should be the same as the
     *            original
     * 
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 1.4
     */
    public static void copyDirectory(File srcDir, File destDir,
            FileFilter filter, boolean preserveFileDate) throws IOException {
        if (srcDir == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (srcDir.exists() == false) {
            throw new FileNotFoundException("Source '" + srcDir
                    + "' does not exist");
        }
        if (srcDir.isDirectory() == false) {
            throw new IOException("Source '" + srcDir
                    + "' exists but is not a directory");
        }
        if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
            throw new IOException("Source '" + srcDir + "' and destination '"
                    + destDir + "' are the same");
        }

        // Cater for destination being directory within the source directory
        // (see IO-141)
        List<String> exclusionList = null;
        if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
            File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir
                    .listFiles(filter);
            if (srcFiles != null && srcFiles.length > 0) {
                exclusionList = new ArrayList<String>(srcFiles.length);
                for (File srcFile : srcFiles) {
                    File copiedFile = new File(destDir, srcFile.getName());
                    exclusionList.add(copiedFile.getCanonicalPath());
                }
            }
        }
        doCopyDirectory(srcDir, destDir, filter, preserveFileDate,
                exclusionList);
    }

    /**
     * Internal copy directory method.
     * 
     * @param srcDir
     *            the validated source directory, must not be <code>null</code>
     * @param destDir
     *            the validated destination directory, must not be
     *            <code>null</code>
     * @param filter
     *            the filter to apply, null means copy all directories and files
     * @param preserveFileDate
     *            whether to preserve the file date
     * @param exclusionList
     *            List of files and directories to exclude from the copy, may be
     *            null
     * @throws IOException
     *             if an error occurs
     * @since Commons IO 1.1
     */
    private static void doCopyDirectory(File srcDir, File destDir,
            FileFilter filter, boolean preserveFileDate,
            List<String> exclusionList) throws IOException {
        // recurse
        File[] files = filter == null ? srcDir.listFiles() : srcDir
                .listFiles(filter);
        if (files == null) { // null if security restricted
            throw new IOException("Failed to list contents of " + srcDir);
        }
        if (destDir.exists()) {
            if (destDir.isDirectory() == false) {
                throw new IOException("Destination '" + destDir
                        + "' exists but is not a directory");
            }
        } else {
            if (destDir.mkdirs() == false) {
                throw new IOException("Destination '" + destDir
                        + "' directory cannot be created");
            }
        }
        if (destDir.canWrite() == false) {
            throw new IOException("Destination '" + destDir
                    + "' cannot be written to");
        }
        for (File file : files) {
            File copiedFile = new File(destDir, file.getName());
            if (exclusionList == null
                    || !exclusionList.contains(file.getCanonicalPath())) {
                if (file.isDirectory()) {
                    doCopyDirectory(file, copiedFile, filter, preserveFileDate,
                            exclusionList);
                } else {
                    doCopyFile(file, copiedFile, preserveFileDate);
                }
            }
        }

        // Do this last, as the above has probably affected directory metadata
        if (preserveFileDate) {
            destDir.setLastModified(srcDir.lastModified());
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Copies bytes from the URL <code>source</code> to a file
     * <code>destination</code>. The directories up to <code>destination</code>
     * will be created if they don't already exist. <code>destination</code>
     * will be overwritten if it already exists.
     * <p>
     * Warning: this method does not set a connection or read timeout and thus
     * might block forever. Use {@link #copyURLToFile(URL, File, int, int)} with
     * reasonable timeouts to prevent this.
     * 
     * @param source
     *            the <code>URL</code> to copy bytes from, must not be
     *            <code>null</code>
     * @param destination
     *            the non-directory <code>File</code> to write bytes to
     *            (possibly overwriting), must not be <code>null</code>
     * @throws IOException
     *             if <code>source</code> URL cannot be opened
     * @throws IOException
     *             if <code>destination</code> is a directory
     * @throws IOException
     *             if <code>destination</code> cannot be written
     * @throws IOException
     *             if <code>destination</code> needs creating but can't be
     * @throws IOException
     *             if an IO error occurs during copying
     */
    public static void copyURLToFile(URL source, File destination)
            throws IOException {
        InputStream input = source.openStream();
        copyInputStreamToFile(input, destination);
    }

    /**
     * Copies bytes from the URL <code>source</code> to a file
     * <code>destination</code>. The directories up to <code>destination</code>
     * will be created if they don't already exist. <code>destination</code>
     * will be overwritten if it already exists.
     * 
     * @param source
     *            the <code>URL</code> to copy bytes from, must not be
     *            <code>null</code>
     * @param destination
     *            the non-directory <code>File</code> to write bytes to
     *            (possibly overwriting), must not be <code>null</code>
     * @param connectionTimeout
     *            the number of milliseconds until this method will timeout if
     *            no connection could be established to the <code>source</code>
     * @param readTimeout
     *            the number of milliseconds until this method will timeout if
     *            no data could be read from the <code>source</code>
     * @throws IOException
     *             if <code>source</code> URL cannot be opened
     * @throws IOException
     *             if <code>destination</code> is a directory
     * @throws IOException
     *             if <code>destination</code> cannot be written
     * @throws IOException
     *             if <code>destination</code> needs creating but can't be
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 2.0
     */
    public static void copyURLToFile(URL source, File destination,
            int connectionTimeout, int readTimeout) throws IOException {
        URLConnection connection = source.openConnection();
        connection.setConnectTimeout(connectionTimeout);
        connection.setReadTimeout(readTimeout);
        InputStream input = connection.getInputStream();
        copyInputStreamToFile(input, destination);
    }

    /**
     * Copies bytes from an {@link InputStream} <code>source</code> to a file
     * <code>destination</code>. The directories up to <code>destination</code>
     * will be created if they don't already exist. <code>destination</code>
     * will be overwritten if it already exists.
     * 
     * @param source
     *            the <code>InputStream</code> to copy bytes from, must not be
     *            <code>null</code>
     * @param destination
     *            the non-directory <code>File</code> to write bytes to
     *            (possibly overwriting), must not be <code>null</code>
     * @throws IOException
     *             if <code>destination</code> is a directory
     * @throws IOException
     *             if <code>destination</code> cannot be written
     * @throws IOException
     *             if <code>destination</code> needs creating but can't be
     * @throws IOException
     *             if an IO error occurs during copying
     * @since Commons IO 2.0
     */
    public static void copyInputStreamToFile(InputStream source,
            File destination) throws IOException {
        try {
            FileOutputStream output = openOutputStream(destination);
            try {
                copy(source, output);
            } finally {
                closeQuietly(output);
            }
        } finally {
            closeQuietly(source);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Deletes a directory recursively.
     * 
     * @param directory
     *            directory to delete
     * @throws IOException
     *             in case deletion is unsuccessful
     */
    public static void deleteDirectory(File directory) throws IOException {
        if (!directory.exists()) {
            return;
        }

        if (!isSymlink(directory)) {
            cleanDirectory(directory);
        }

        if (!directory.delete()) {
            String message = "Unable to delete directory " + directory + ".";
            throw new IOException(message);
        }
    }

    /**
     * Deletes a file, never throwing an exception. If file is a directory,
     * delete it and all sub-directories.
     * <p>
     * The difference between File.delete() and this method are:
     * <ul>
     * <li>A directory to be deleted does not have to be empty.</li>
     * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
     * </ul>
     * 
     * @param file
     *            file or directory to delete, can be <code>null</code>
     * @return <code>true</code> if the file or directory was deleted, otherwise
     *         <code>false</code>
     * 
     * @since Commons IO 1.4
     */
    public static boolean deleteQuietly(File file) {
        if (file == null) {
            return false;
        }
        try {
            if (file.isDirectory()) {
                cleanDirectory(file);
            }
        } catch (Exception ignored) {
        }

        try {
            return file.delete();
        } catch (Exception ignored) {
            return false;
        }
    }

    /**
     * Cleans a directory without deleting it.
     * 
     * @param directory
     *            directory to clean
     * @throws IOException
     *             in case cleaning is unsuccessful
     */
    public static void cleanDirectory(File directory) throws IOException {
        if (!directory.exists()) {
            String message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (!directory.isDirectory()) {
            String message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        }

        File[] files = directory.listFiles();
        if (files == null) { // null if security restricted
            throw new IOException("Failed to list contents of " + directory);
        }

        IOException exception = null;
        for (File file : files) {
            try {
                forceDelete(file);
            } catch (IOException ioe) {
                exception = ioe;
            }
        }

        if (null != exception) {
            throw exception;
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Waits for NFS to propagate a file creation, imposing a timeout.
     * <p>
     * This method repeatedly tests {@link File#exists()} until it returns true
     * up to the maximum time specified in seconds.
     * 
     * @param file
     *            the file to check, must not be <code>null</code>
     * @param seconds
     *            the maximum time in seconds to wait
     * @return true if file exists
     * @throws NullPointerException
     *             if the file is <code>null</code>
     */
    public static boolean waitFor(File file, int seconds) {
        int timeout = 0;
        int tick = 0;
        while (!file.exists()) {
            if (tick++ >= 10) {
                tick = 0;
                if (timeout++ > seconds) {
                    return false;
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException ignore) {
                // ignore exception
            } catch (Exception ex) {
                break;
            }
        }
        return true;
    }

    // -----------------------------------------------------------------------
    /**
     * Reads the contents of a file into a String. The file is always closed.
     * 
     * @param file
     *            the file to read, must not be <code>null</code>
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @return the file contents, never <code>null</code>
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     */
    public static String readFileToString(File file, String encoding)
            throws IOException {
        InputStream in = null;
        try {
            in = openInputStream(file);
            return inputStreamToString(in, encoding);
        } finally {
            closeQuietly(in);
        }
    }

    /**
     * Reads the contents of a file into a String using the default encoding for
     * the VM. The file is always closed.
     * 
     * @param file
     *            the file to read, must not be <code>null</code>
     * @return the file contents, never <code>null</code>
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.3.1
     */
    public static String readFileToString(File file) throws IOException {
        return readFileToString(file, null);
    }

    /**
     * Reads the contents of a file into a byte array. The file is always
     * closed.
     * 
     * @param file
     *            the file to read, must not be <code>null</code>
     * @return the file contents, never <code>null</code>
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.1
     */
    public static byte[] readFileToByteArray(File file) throws IOException {
        InputStream in = null;
        try {
            in = openInputStream(file);
            return toByteArray(in);
        } finally {
            closeQuietly(in);
        }
    }

    /**
     * Reads the contents of a file line by line to a List of Strings. The file
     * is always closed.
     * 
     * @param file
     *            the file to read, must not be <code>null</code>
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @return the list of Strings representing each line in the file, never
     *         <code>null</code>
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     * @since Commons IO 1.1
     */
    public static List<String> readLines(File file, String encoding)
            throws IOException {
        InputStream in = null;
        try {
            in = openInputStream(file);
            return readLines(in, encoding);
        } finally {
            closeQuietly(in);
        }
    }

    /**
     * Reads the contents of a file line by line to a List of Strings using the
     * default encoding for the VM. The file is always closed.
     * 
     * @param file
     *            the file to read, must not be <code>null</code>
     * @return the list of Strings representing each line in the file, never
     *         <code>null</code>
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.3
     */
    public static List<String> readLines(File file) throws IOException {
        return readLines(file, null);
    }

    /**
     * Returns an Iterator for the lines in a <code>File</code>.
     * <p>
     * This method opens an <code>InputStream</code> for the file. When you have
     * finished with the iterator you should close the stream to free internal
     * resources. This can be done by calling the {@link LineIterator#close()}
     * or {@link LineIterator#closeQuietly(LineIterator)} method.
     * <p>
     * The recommended usage pattern is:
     * 
     * <pre>
     * LineIterator it = FileUtils.lineIterator(file, &quot;UTF-8&quot;);
     * try {
     *     while (it.hasNext()) {
     *         String line = it.nextLine();
     *         // / do something with line
     *     }
     * } finally {
     *     LineIterator.closeQuietly(iterator);
     * }
     * </pre>
     * <p>
     * If an exception occurs during the creation of the iterator, the
     * underlying stream is closed.
     * 
     * @param file
     *            the file to open for input, must not be <code>null</code>
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @return an Iterator of the lines in the file, never <code>null</code>
     * @throws IOException
     *             in case of an I/O error (file closed)
     * @since Commons IO 1.2
     */
    public static LineIterator lineIterator(File file, String encoding)
            throws IOException {
        InputStream in = null;
        try {
            in = openInputStream(file);
            return lineIterator(in, encoding);
        } catch (IOException ex) {
            closeQuietly(in);
            throw ex;
        } catch (RuntimeException ex) {
            closeQuietly(in);
            throw ex;
        }
    }

    /**
     * Returns an Iterator for the lines in a <code>File</code> using the
     * default encoding for the VM.
     * 
     * @param file
     *            the file to open for input, must not be <code>null</code>
     * @return an Iterator of the lines in the file, never <code>null</code>
     * @throws IOException
     *             in case of an I/O error (file closed)
     * @since Commons IO 1.3
     * @see #lineIterator(File, String)
     */
    public static LineIterator lineIterator(File file) throws IOException {
        return lineIterator(file, null);
    }

    // -----------------------------------------------------------------------
    /**
     * Writes a String to a file creating the file if it does not exist.
     * 
     * NOTE: As from v1.3, the parent directories of the file will be created if
     * they do not exist.
     * 
     * @param file
     *            the file to write
     * @param data
     *            the content to write to the file
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     */
    public static void writeStringToFile(File file, String data, String encoding)
            throws IOException {
        OutputStream out = null;
        try {
            out = openOutputStream(file);
            writeToOutputStream(data, out, encoding);
        } finally {
            closeQuietly(out);
        }
    }

    /**
     * Writes a String to a file creating the file if it does not exist using
     * the default encoding for the VM.
     * 
     * @param file
     *            the file to write
     * @param data
     *            the content to write to the file
     * @throws IOException
     *             in case of an I/O error
     */
    public static void writeStringToFile(File file, String data)
            throws IOException {
        writeStringToFile(file, data, null);
    }

    /**
     * Writes a CharSequence to a file creating the file if it does not exist
     * using the default encoding for the VM.
     * 
     * @param file
     *            the file to write
     * @param data
     *            the content to write to the file
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 2.0
     */
    public static void write(File file, CharSequence data) throws IOException {
        String str = data == null ? null : data.toString();
        writeStringToFile(file, str);
    }

    /**
     * Writes a CharSequence to a file creating the file if it does not exist.
     * 
     * @param file
     *            the file to write
     * @param data
     *            the content to write to the file
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     * @since Commons IO 2.0
     */
    public static void write(File file, CharSequence data, String encoding)
            throws IOException {
        String str = data == null ? null : data.toString();
        writeStringToFile(file, str, encoding);
    }

    /**
     * Writes a byte array to a file creating the file if it does not exist.
     * <p>
     * NOTE: As from v1.3, the parent directories of the file will be created if
     * they do not exist.
     * 
     * @param file
     *            the file to write to
     * @param data
     *            the content to write to the file
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.1
     */
    public static void writeByteArrayToFile(File file, byte[] data)
            throws IOException {
        OutputStream out = null;
        try {
            out = openOutputStream(file);
            out.write(data);
        } finally {
            closeQuietly(out);
        }
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * the specified <code>File</code> line by line. The specified character
     * encoding and the default line ending will be used.
     * <p>
     * NOTE: As from v1.3, the parent directories of the file will be created if
     * they do not exist.
     * 
     * @param file
     *            the file to write to
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @param lines
     *            the lines to write, <code>null</code> entries produce blank
     *            lines
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     * @since Commons IO 1.1
     */
    public static void writeLines(File file, String encoding,
            Collection<?> lines) throws IOException {
        writeLines(file, encoding, lines, null);
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * the specified <code>File</code> line by line. The default VM encoding and
     * the default line ending will be used.
     * 
     * @param file
     *            the file to write to
     * @param lines
     *            the lines to write, <code>null</code> entries produce blank
     *            lines
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.3
     */
    public static void writeLines(File file, Collection<?> lines)
            throws IOException {
        writeLines(file, null, lines, null);
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * the specified <code>File</code> line by line. The specified character
     * encoding and the line ending will be used.
     * <p>
     * NOTE: As from v1.3, the parent directories of the file will be created if
     * they do not exist.
     * 
     * @param file
     *            the file to write to
     * @param encoding
     *            the encoding to use, <code>null</code> means platform default
     * @param lines
     *            the lines to write, <code>null</code> entries produce blank
     *            lines
     * @param lineEnding
     *            the line separator to use, <code>null</code> is system default
     * @throws IOException
     *             in case of an I/O error
     * @throws java.io.UnsupportedEncodingException
     *             if the encoding is not supported by the VM
     * @since Commons IO 1.1
     */
    public static void writeLines(File file, String encoding,
            Collection<?> lines, String lineEnding) throws IOException {
        OutputStream out = null;
        try {
            out = openOutputStream(file);
            writeLines(lines, lineEnding, out, encoding);
        } finally {
            closeQuietly(out);
        }
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * the specified <code>File</code> line by line. The default VM encoding and
     * the specified line ending will be used.
     * 
     * @param file
     *            the file to write to
     * @param lines
     *            the lines to write, <code>null</code> entries produce blank
     *            lines
     * @param lineEnding
     *            the line separator to use, <code>null</code> is system default
     * @throws IOException
     *             in case of an I/O error
     * @since Commons IO 1.3
     */
    public static void writeLines(File file, Collection<?> lines,
            String lineEnding) throws IOException {
        writeLines(file, null, lines, lineEnding);
    }

    // -----------------------------------------------------------------------
    /**
     * Deletes a file. If file is a directory, delete it and all
     * sub-directories.
     * <p>
     * The difference between File.delete() and this method are:
     * <ul>
     * <li>A directory to be deleted does not have to be empty.</li>
     * <li>You get exceptions when a file or directory cannot be deleted.
     * (java.io.File methods returns a boolean)</li>
     * </ul>
     * 
     * @param file
     *            file or directory to delete, must not be <code>null</code>
     * @throws NullPointerException
     *             if the directory is <code>null</code>
     * @throws FileNotFoundException
     *             if the file was not found
     * @throws IOException
     *             in case deletion is unsuccessful
     */
    public static void forceDelete(File file) throws IOException {
        if (file.isDirectory()) {
            deleteDirectory(file);
        } else {
            boolean filePresent = file.exists();
            if (!file.delete()) {
                if (!filePresent) {
                    throw new FileNotFoundException("File does not exist: "
                            + file);
                }
                String message = "Unable to delete file: " + file;
                throw new IOException(message);
            }
        }
    }

    /**
     * Schedules a file to be deleted when JVM exits. If file is directory
     * delete it and all sub-directories.
     * 
     * @param file
     *            file or directory to delete, must not be <code>null</code>
     * @throws NullPointerException
     *             if the file is <code>null</code>
     * @throws IOException
     *             in case deletion is unsuccessful
     */
    public static void forceDeleteOnExit(File file) throws IOException {
        if (file.isDirectory()) {
            deleteDirectoryOnExit(file);
        } else {
            file.deleteOnExit();
        }
    }

    /**
     * Schedules a directory recursively for deletion on JVM exit.
     * 
     * @param directory
     *            directory to delete, must not be <code>null</code>
     * @throws NullPointerException
     *             if the directory is <code>null</code>
     * @throws IOException
     *             in case deletion is unsuccessful
     */
    private static void deleteDirectoryOnExit(File directory)
            throws IOException {
        if (!directory.exists()) {
            return;
        }

        if (!isSymlink(directory)) {
            cleanDirectoryOnExit(directory);
        }
        directory.deleteOnExit();
    }

    /**
     * Cleans a directory without deleting it.
     * 
     * @param directory
     *            directory to clean, must not be <code>null</code>
     * @throws NullPointerException
     *             if the directory is <code>null</code>
     * @throws IOException
     *             in case cleaning is unsuccessful
     */
    private static void cleanDirectoryOnExit(File directory) throws IOException {
        if (!directory.exists()) {
            String message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (!directory.isDirectory()) {
            String message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        }

        File[] files = directory.listFiles();
        if (files == null) { // null if security restricted
            throw new IOException("Failed to list contents of " + directory);
        }

        IOException exception = null;
        for (File file : files) {
            try {
                forceDeleteOnExit(file);
            } catch (IOException ioe) {
                exception = ioe;
            }
        }

        if (null != exception) {
            throw exception;
        }
    }

    /**
     * Makes a directory, including any necessary but nonexistent parent
     * directories. If a file already exists with specified name but it is not a
     * directory then an IOException is thrown. If the directory cannot be
     * created (or does not already exist) then an IOException is thrown.
     * 
     * @param directory
     *            directory to create, must not be <code>null</code>
     * @throws NullPointerException
     *             if the directory is <code>null</code>
     * @throws IOException
     *             if the directory cannot be created or the file already exists
     *             but is not a directory
     */
    public static void forceMkdir(File directory) throws IOException {
        if (directory.exists()) {
            if (!directory.isDirectory()) {
                String message = "File " + directory + " exists and is "
                        + "not a directory. Unable to create directory.";
                throw new IOException(message);
            }
        } else {
            if (!directory.mkdirs()) {
                // Double-check that some other thread or process hasn't made
                // the directory in the background
                if (!directory.isDirectory()) {
                    String message = "Unable to create directory " + directory;
                    throw new IOException(message);
                }
            }
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Returns the size of the specified file or directory. If the provided
     * {@link File} is a regular file, then the file's length is returned. If
     * the argument is a directory, then the size of the directory is calculated
     * recursively. If a directory or subdirectory is security restricted, its
     * size will not be included.
     * 
     * @param file
     *            the regular file or directory to return the size of (must not
     *            be <code>null</code>).
     * 
     * @return the length of the file, or recursive size of the directory,
     *         provided (in bytes).
     * 
     * @throws NullPointerException
     *             if the file is <code>null</code>
     * @throws IllegalArgumentException
     *             if the file does not exist.
     * 
     * @since Commons IO 2.0
     */
    public static long sizeOf(File file) {

        if (!file.exists()) {
            String message = file + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (file.isDirectory()) {
            return sizeOfDirectory(file);
        } else {
            return file.length();
        }

    }

    /**
     * Counts the size of a directory recursively (sum of the length of all
     * files).
     * 
     * @param directory
     *            directory to inspect, must not be <code>null</code>
     * @return size of directory in bytes, 0 if directory is security restricted
     * @throws NullPointerException
     *             if the directory is <code>null</code>
     */
    public static long sizeOfDirectory(File directory) {
        if (!directory.exists()) {
            String message = directory + " does not exist";
            throw new IllegalArgumentException(message);
        }

        if (!directory.isDirectory()) {
            String message = directory + " is not a directory";
            throw new IllegalArgumentException(message);
        }

        long size = 0;

        File[] files = directory.listFiles();
        if (files == null) { // null if security restricted
            return 0L;
        }
        for (File file : files) {
            size += sizeOf(file);
        }

        return size;
    }

    // -----------------------------------------------------------------------
    /**
     * Tests if the specified <code>File</code> is newer than the reference
     * <code>File</code>.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param reference
     *            the <code>File</code> of which the modification date is used,
     *            must not be <code>null</code>
     * @return true if the <code>File</code> exists and has been modified more
     *         recently than the reference <code>File</code>
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     * @throws IllegalArgumentException
     *             if the reference file is <code>null</code> or doesn't exist
     */
    public static boolean isFileNewer(File file, File reference) {
        if (reference == null) {
            throw new IllegalArgumentException("No specified reference file");
        }
        if (!reference.exists()) {
            throw new IllegalArgumentException("The reference file '"
                    + reference + "' doesn't exist");
        }
        return isFileNewer(file, reference.lastModified());
    }

    /**
     * Tests if the specified <code>File</code> is newer than the specified
     * <code>Date</code>.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param date
     *            the date reference, must not be <code>null</code>
     * @return true if the <code>File</code> exists and has been modified after
     *         the given <code>Date</code>.
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     */
    public static boolean isFileNewer(File file, Date date) {
        if (date == null) {
            throw new IllegalArgumentException("No specified date");
        }
        return isFileNewer(file, date.getTime());
    }

    /**
     * Tests if the specified <code>File</code> is newer than the specified time
     * reference.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param timeMillis
     *            the time reference measured in milliseconds since the epoch
     *            (00:00:00 GMT, January 1, 1970)
     * @return true if the <code>File</code> exists and has been modified after
     *         the given time reference.
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     */
    public static boolean isFileNewer(File file, long timeMillis) {
        if (file == null) {
            throw new IllegalArgumentException("No specified file");
        }
        if (!file.exists()) {
            return false;
        }
        return file.lastModified() > timeMillis;
    }

    // -----------------------------------------------------------------------
    /**
     * Tests if the specified <code>File</code> is older than the reference
     * <code>File</code>.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param reference
     *            the <code>File</code> of which the modification date is used,
     *            must not be <code>null</code>
     * @return true if the <code>File</code> exists and has been modified before
     *         the reference <code>File</code>
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     * @throws IllegalArgumentException
     *             if the reference file is <code>null</code> or doesn't exist
     */
    public static boolean isFileOlder(File file, File reference) {
        if (reference == null) {
            throw new IllegalArgumentException("No specified reference file");
        }
        if (!reference.exists()) {
            throw new IllegalArgumentException("The reference file '"
                    + reference + "' doesn't exist");
        }
        return isFileOlder(file, reference.lastModified());
    }

    /**
     * Tests if the specified <code>File</code> is older than the specified
     * <code>Date</code>.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param date
     *            the date reference, must not be <code>null</code>
     * @return true if the <code>File</code> exists and has been modified before
     *         the given <code>Date</code>.
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     * @throws IllegalArgumentException
     *             if the date is <code>null</code>
     */
    public static boolean isFileOlder(File file, Date date) {
        if (date == null) {
            throw new IllegalArgumentException("No specified date");
        }
        return isFileOlder(file, date.getTime());
    }

    /**
     * Tests if the specified <code>File</code> is older than the specified time
     * reference.
     * 
     * @param file
     *            the <code>File</code> of which the modification date must be
     *            compared, must not be <code>null</code>
     * @param timeMillis
     *            the time reference measured in milliseconds since the epoch
     *            (00:00:00 GMT, January 1, 1970)
     * @return true if the <code>File</code> exists and has been modified before
     *         the given time reference.
     * @throws IllegalArgumentException
     *             if the file is <code>null</code>
     */
    public static boolean isFileOlder(File file, long timeMillis) {
        if (file == null) {
            throw new IllegalArgumentException("No specified file");
        }
        if (!file.exists()) {
            return false;
        }
        return file.lastModified() < timeMillis;
    }

    // -----------------------------------------------------------------------
    /**
     * Computes the checksum of a file using the CRC32 checksum routine. The
     * value of the checksum is returned.
     * 
     * @param file
     *            the file to checksum, must not be <code>null</code>
     * @return the checksum value
     * @throws NullPointerException
     *             if the file or checksum is <code>null</code>
     * @throws IllegalArgumentException
     *             if the file is a directory
     * @throws IOException
     *             if an IO error occurs reading the file
     * @since Commons IO 1.3
     */
    public static long checksumCRC32(File file) throws IOException {
        CRC32 crc = new CRC32();
        checksum(file, crc);
        return crc.getValue();
    }

    /**
     * Computes the checksum of a file using the specified checksum object.
     * Multiple files may be checked using one <code>Checksum</code> instance if
     * desired simply by reusing the same checksum object. For example:
     * 
     * <pre>
     * long csum = FileUtils.checksum(file, new CRC32()).getValue();
     * </pre>
     * 
     * @param file
     *            the file to checksum, must not be <code>null</code>
     * @param checksum
     *            the checksum object to be used, must not be <code>null</code>
     * @return the checksum specified, updated with the content of the file
     * @throws NullPointerException
     *             if the file or checksum is <code>null</code>
     * @throws IllegalArgumentException
     *             if the file is a directory
     * @throws IOException
     *             if an IO error occurs reading the file
     * @since Commons IO 1.3
     */
    public static Checksum checksum(File file, Checksum checksum)
            throws IOException {
        if (file.isDirectory()) {
            throw new IllegalArgumentException(
                    "Checksums can't be computed on directories");
        }
        InputStream in = null;
        try {
            in = new CheckedInputStream(new FileInputStream(file), checksum);
            copy(in, new NullOutputStream());
        } finally {
            closeQuietly(in);
        }
        return checksum;
    }

    /**
     * Moves a directory.
     * <p>
     * When the destination directory is on another file system, do a
     * "copy and delete".
     * 
     * @param srcDir
     *            the directory to be moved
     * @param destDir
     *            the destination directory
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs moving the file
     * @since Commons IO 1.4
     */
    public static void moveDirectory(File srcDir, File destDir)
            throws IOException {
        if (srcDir == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (!srcDir.exists()) {
            throw new FileNotFoundException("Source '" + srcDir
                    + "' does not exist");
        }
        if (!srcDir.isDirectory()) {
            throw new IOException("Source '" + srcDir + "' is not a directory");
        }
        if (destDir.exists()) {
            throw new FileExistsException("Destination '" + destDir
                    + "' already exists");
        }
        boolean rename = srcDir.renameTo(destDir);
        if (!rename) {
            copyDirectory(srcDir, destDir);
            deleteDirectory(srcDir);
            if (srcDir.exists()) {
                throw new IOException("Failed to delete original directory '"
                        + srcDir + "' after copy to '" + destDir + "'");
            }
        }
    }

    /**
     * Moves a directory to another directory.
     * 
     * @param src
     *            the file to be moved
     * @param destDir
     *            the destination file
     * @param createDestDir
     *            If <code>true</code> create the destination directory,
     *            otherwise if <code>false</code> throw an IOException
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs moving the file
     * @since Commons IO 1.4
     */
    public static void moveDirectoryToDirectory(File src, File destDir,
            boolean createDestDir) throws IOException {
        if (src == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException(
                    "Destination directory must not be null");
        }
        if (!destDir.exists() && createDestDir) {
            destDir.mkdirs();
        }
        if (!destDir.exists()) {
            throw new FileNotFoundException("Destination directory '" + destDir
                    + "' does not exist [createDestDir=" + createDestDir + "]");
        }
        if (!destDir.isDirectory()) {
            throw new IOException("Destination '" + destDir
                    + "' is not a directory");
        }
        moveDirectory(src, new File(destDir, src.getName()));

    }

    /**
     * Moves a file.
     * <p>
     * When the destination file is on another file system, do a
     * "copy and delete".
     * 
     * @param srcFile
     *            the file to be moved
     * @param destFile
     *            the destination file
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs moving the file
     * @since Commons IO 1.4
     */
    public static void moveFile(File srcFile, File destFile) throws IOException {
        if (srcFile == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destFile == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (!srcFile.exists()) {
            throw new FileNotFoundException("Source '" + srcFile
                    + "' does not exist");
        }
        if (srcFile.isDirectory()) {
            throw new IOException("Source '" + srcFile + "' is a directory");
        }
        if (destFile.exists()) {
            throw new FileExistsException("Destination '" + destFile
                    + "' already exists");
        }
        if (destFile.isDirectory()) {
            throw new IOException("Destination '" + destFile
                    + "' is a directory");
        }
        boolean rename = srcFile.renameTo(destFile);
        if (!rename) {
            copyFile(srcFile, destFile);
            if (!srcFile.delete()) {
                deleteQuietly(destFile);
                throw new IOException("Failed to delete original file '"
                        + srcFile + "' after copy to '" + destFile + "'");
            }
        }
    }

    /**
     * Moves a file to a directory.
     * 
     * @param srcFile
     *            the file to be moved
     * @param destDir
     *            the destination file
     * @param createDestDir
     *            If <code>true</code> create the destination directory,
     *            otherwise if <code>false</code> throw an IOException
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs moving the file
     * @since Commons IO 1.4
     */
    public static void moveFileToDirectory(File srcFile, File destDir,
            boolean createDestDir) throws IOException {
        if (srcFile == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException(
                    "Destination directory must not be null");
        }
        if (!destDir.exists() && createDestDir) {
            destDir.mkdirs();
        }
        if (!destDir.exists()) {
            throw new FileNotFoundException("Destination directory '" + destDir
                    + "' does not exist [createDestDir=" + createDestDir + "]");
        }
        if (!destDir.isDirectory()) {
            throw new IOException("Destination '" + destDir
                    + "' is not a directory");
        }
        moveFile(srcFile, new File(destDir, srcFile.getName()));
    }

    /**
     * Moves a file or directory to the destination directory.
     * <p>
     * When the destination is on another file system, do a "copy and delete".
     * 
     * @param src
     *            the file or directory to be moved
     * @param destDir
     *            the destination directory
     * @param createDestDir
     *            If <code>true</code> create the destination directory,
     *            otherwise if <code>false</code> throw an IOException
     * @throws NullPointerException
     *             if source or destination is <code>null</code>
     * @throws IOException
     *             if source or destination is invalid
     * @throws IOException
     *             if an IO error occurs moving the file
     * @since Commons IO 1.4
     */
    public static void moveToDirectory(File src, File destDir,
            boolean createDestDir) throws IOException {
        if (src == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (!src.exists()) {
            throw new FileNotFoundException("Source '" + src
                    + "' does not exist");
        }
        if (src.isDirectory()) {
            moveDirectoryToDirectory(src, destDir, createDestDir);
        } else {
            moveFileToDirectory(src, destDir, createDestDir);
        }
    }

    /**
     * Determines whether the specified file is a Symbolic Link rather than an
     * actual file.
     * <p>
     * Will not return true if there is a Symbolic Link anywhere in the path,
     * only if the specific file is.
     * 
     * @param file
     *            the file to check
     * @return true if the file is a Symbolic Link
     * @throws IOException
     *             if an IO error occurs while checking the file
     * @since Commons IO 2.0
     */
    public static boolean isSymlink(File file) throws IOException {
        if (file == null) {
            throw new NullPointerException("File must not be null");
        }
        if (isSystemWindows()) {
            return false;
        }
        File fileInCanonicalDir = null;
        if (file.getParent() == null) {
            fileInCanonicalDir = file;
        } else {
            File canonicalDir = file.getParentFile().getCanonicalFile();
            fileInCanonicalDir = new File(canonicalDir, file.getName());
        }

        if (fileInCanonicalDir.getCanonicalFile().equals(
                fileInCanonicalDir.getAbsoluteFile())) {
            return false;
        } else {
            return true;
        }
    }

    // ///////////////////////////////////////////
    //
    // IOUtils
    //
    // ///////////////////////////////////////////

    /**
     * The Unix directory separator character.
     */
    public static final char DIR_SEPARATOR_UNIX = '/';
    /**
     * The Windows directory separator character.
     */
    public static final char DIR_SEPARATOR_WINDOWS = '\\';
    /**
     * The system directory separator character.
     */
    public static final char DIR_SEPARATOR = File.separatorChar;
    /**
     * The Unix line separator string.
     */
    public static final String LINE_SEPARATOR_UNIX = "\n";
    /**
     * The Windows line separator string.
     */
    public static final String LINE_SEPARATOR_WINDOWS = "\r\n";

    /**
     * The default buffer size to use for
     * {@link #copyLarge(InputStream, OutputStream)} and
     * {@link #copyLarge(Reader, Writer)}
     */
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    /**
     * The default buffer size to use for the skip() methods.
     */
    private static final int SKIP_BUFFER_SIZE = 2048;

    // Allocated in the skip method if necessary.
    private static char[] SKIP_CHAR_BUFFER;
    private static byte[] SKIP_BYTE_BUFFER;

    public static void closeQuietly(Connection connection) {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
        }
    }

    public static void closeQuietly(Statement statement) {
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
        }
    }

    public static void closeQuietly(ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
        }
    }

    /**
     * Unconditionally close an <code>Reader</code>.
     * <p>
     * Equivalent to {@link Reader#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * char[] data = new char[1024];
     * Reader in = null;
     * try {
     *     in = new FileReader(&quot;foo.txt&quot;);
     *     in.read(data);
     *     in.close(); // close errors are handled
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(in);
     * }
     * </pre>
     * 
     * @param input
     *            the Reader to close, may be null or already closed
     */
    public static void closeQuietly(Reader input) {
        closeQuietly((Closeable) input);
    }

    /**
     * Unconditionally close a <code>Writer</code>.
     * <p>
     * Equivalent to {@link Writer#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * Writer out = null;
     * try {
     *     out = new StringWriter();
     *     out.write(&quot;Hello World&quot;);
     *     out.close(); // close errors are handled
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(out);
     * }
     * </pre>
     * 
     * @param output
     *            the Writer to close, may be null or already closed
     */
    public static void closeQuietly(Writer output) {
        closeQuietly((Closeable) output);
    }

    /**
     * Unconditionally close an <code>InputStream</code>.
     * <p>
     * Equivalent to {@link InputStream#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * byte[] data = new byte[1024];
     * InputStream in = null;
     * try {
     *     in = new FileInputStream(&quot;foo.txt&quot;);
     *     in.read(data);
     *     in.close(); // close errors are handled
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(in);
     * }
     * </pre>
     * 
     * @param input
     *            the InputStream to close, may be null or already closed
     */
    public static void closeQuietly(InputStream input) {
        closeQuietly((Closeable) input);
    }

    /**
     * Unconditionally close an <code>OutputStream</code>.
     * <p>
     * Equivalent to {@link OutputStream#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * byte[] data = &quot;Hello, World&quot;.getBytes();
     * 
     * OutputStream out = null;
     * try {
     *     out = new FileOutputStream(&quot;foo.txt&quot;);
     *     out.write(data);
     *     out.close(); // close errors are handled
     * } catch (IOException e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(out);
     * }
     * </pre>
     * 
     * @param output
     *            the OutputStream to close, may be null or already closed
     */
    public static void closeQuietly(OutputStream output) {
        closeQuietly((Closeable) output);
    }

    /**
     * Unconditionally close a <code>Closeable</code>.
     * <p>
     * Equivalent to {@link Closeable#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * Closeable closeable = null;
     * try {
     *     closeable = new FileReader(&quot;foo.txt&quot;);
     *     // process closeable
     *     closeable.close();
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(closeable);
     * }
     * </pre>
     * 
     * @param closeable
     *            the object to close, may be null or already closed
     * @since Commons IO 2.0
     */
    public static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (IOException ioe) {
            // ignore
        }
    }

    /**
     * Unconditionally close a <code>Socket</code>.
     * <p>
     * Equivalent to {@link Socket#close()}, except any exceptions will be
     * ignored. This is typically used in finally blocks.
     * <p>
     * Example code:
     * 
     * <pre>
     * Socket socket = null;
     * try {
     *     socket = new Socket(&quot;http://www.foo.com/&quot;, 80);
     *     // process socket
     *     socket.close();
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(socket);
     * }
     * </pre>
     * 
     * @param sock
     *            the Socket to close, may be null or already closed
     * @since Commons IO 2.0
     */
    public static void closeQuietly(Socket sock) {
        if (sock != null) {
            try {
                sock.close();
            } catch (IOException ioe) {
                // ignored
            }
        }
    }

    /**
     * Fetches entire contents of an <code>InputStream</code> and represent same
     * data as result InputStream.
     * <p>
     * This method is useful where,
     * <ul>
     * <li>Source InputStream is slow.</li>
     * <li>It has network resources associated, so we cannot keep it open for
     * long time.</li>
     * <li>It has network timeout associated.</li>
     * </ul>
     * It can be used in favor of {@link #toByteArray(InputStream)}, since it
     * avoids unnecessary allocation and copy of byte[].<br>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            Stream to be fully buffered.
     * @return A fully buffered stream.
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 2.0
     */
    public static InputStream toBufferedInputStream(InputStream input)
            throws IOException {
        return com.clark.io.ByteArrayOutputStream.toBufferedInputStream(input);
    }

    // read toByteArray
    // -----------------------------------------------------------------------
    /**
     * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @return the requested byte array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    /**
     * Get the contents of a <code>Reader</code> as a <code>byte[]</code> using
     * the default character encoding of the platform.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @return the requested byte array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static byte[] toByteArray(Reader input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output);
        return output.toByteArray();
    }

    /**
     * Get the contents of a <code>Reader</code> as a <code>byte[]</code> using
     * the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the requested byte array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static byte[] toByteArray(Reader input, String encoding)
            throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        copy(input, output, encoding);
        return output.toByteArray();
    }

    /**
     * Get the contents of a <code>String</code> as a <code>byte[]</code> using
     * the default character encoding of the platform.
     * <p>
     * This is the same as {@link String#getBytes()}.
     * 
     * @param input
     *            the <code>String</code> to convert
     * @return the requested byte array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs (never occurs)
     * @deprecated Use {@link String#getBytes()}
     */
    @Deprecated
    public static byte[] toByteArray(String input) throws IOException {
        return input.getBytes();
    }

    // read char[]
    // -----------------------------------------------------------------------
    /**
     * Get the contents of an <code>InputStream</code> as a character array
     * using the default character encoding of the platform.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param is
     *            the <code>InputStream</code> to read from
     * @return the requested character array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static char[] toCharArray(InputStream is) throws IOException {
        CharArrayWriter output = new CharArrayWriter();
        copy(is, output);
        return output.toCharArray();
    }

    /**
     * Get the contents of an <code>InputStream</code> as a character array
     * using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param is
     *            the <code>InputStream</code> to read from
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the requested character array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static char[] toCharArray(InputStream is, String encoding)
            throws IOException {
        CharArrayWriter output = new CharArrayWriter();
        copy(is, output, encoding);
        return output.toCharArray();
    }

    /**
     * Get the contents of a <code>Reader</code> as a character array.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @return the requested character array
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static char[] toCharArray(Reader input) throws IOException {
        CharArrayWriter sw = new CharArrayWriter();
        copy(input, sw);
        return sw.toCharArray();
    }

    // read toString
    // -----------------------------------------------------------------------
    /**
     * Get the contents of an <code>InputStream</code> as a String using the
     * default character encoding of the platform.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @return the requested String
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static String inputStreamToString(InputStream input)
            throws IOException {
        StringBuilderWriter sw = new StringBuilderWriter();
        copy(input, sw);
        return sw.toString();
    }

    /**
     * Get the contents of an <code>InputStream</code> as a String using the
     * specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the requested String
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static String inputStreamToString(InputStream input, String encoding)
            throws IOException {
        StringBuilderWriter sw = new StringBuilderWriter();
        copy(input, sw, encoding);
        return sw.toString();
    }

    /**
     * Get the contents of a <code>Reader</code> as a String.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @return the requested String
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static String readerToString(Reader input) throws IOException {
        StringBuilderWriter sw = new StringBuilderWriter();
        copy(input, sw);
        return sw.toString();
    }

    /**
     * Get the contents of a <code>byte[]</code> as a String using the default
     * character encoding of the platform.
     * 
     * @param input
     *            the byte array to read from
     * @return the requested String
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs (never occurs)
     * @deprecated Use {@link String#String(byte[])}
     */
    @Deprecated
    public static String byteArrayToString(byte[] input) throws IOException {
        return new String(input);
    }

    /**
     * Get the contents of a <code>byte[]</code> as a String using the specified
     * character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * 
     * @param input
     *            the byte array to read from
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the requested String
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs (never occurs)
     * @deprecated Use {@link String#String(byte[],String)}
     */
    @Deprecated
    public static String byteArrayToString(byte[] input, String encoding)
            throws IOException {
        if (encoding == null) {
            return new String(input);
        } else {
            return new String(input, encoding);
        }
    }

    // readLines
    // -----------------------------------------------------------------------
    /**
     * Get the contents of an <code>InputStream</code> as a list of Strings, one
     * entry per line, using the default character encoding of the platform.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static List<String> readLines(InputStream input) throws IOException {
        InputStreamReader reader = new InputStreamReader(input);
        return readLines(reader);
    }

    /**
     * Get the contents of an <code>InputStream</code> as a list of Strings, one
     * entry per line, using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static List<String> readLines(InputStream input, String encoding)
            throws IOException {
        if (encoding == null) {
            return readLines(input);
        } else {
            InputStreamReader reader = new InputStreamReader(input, encoding);
            return readLines(reader);
        }
    }

    /**
     * Get the contents of a <code>Reader</code> as a list of Strings, one entry
     * per line.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from, not null
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static List<String> readLines(Reader input) throws IOException {
        BufferedReader reader = new BufferedReader(input);
        List<String> list = new ArrayList<String>();
        String line = reader.readLine();
        while (line != null) {
            list.add(line);
            line = reader.readLine();
        }
        return list;
    }

    // lineIterator
    // -----------------------------------------------------------------------
    /**
     * Return an Iterator for the lines in a <code>Reader</code>.
     * <p>
     * <code>LineIterator</code> holds a reference to the open
     * <code>Reader</code> specified here. When you have finished with the
     * iterator you should close the reader to free internal resources. This can
     * be done by closing the reader directly, or by calling
     * {@link LineIterator#close()} or
     * {@link LineIterator#closeQuietly(LineIterator)}.
     * <p>
     * The recommended usage pattern is:
     * 
     * <pre>
     * try {
     *     LineIterator it = IOUtils.lineIterator(reader);
     *     while (it.hasNext()) {
     *         String line = it.nextLine();
     *         // / do something with line
     *     }
     * } finally {
     *     IOUtils.closeQuietly(reader);
     * }
     * </pre>
     * 
     * @param reader
     *            the <code>Reader</code> to read from, not null
     * @return an Iterator of the lines in the reader, never null
     * @throws IllegalArgumentException
     *             if the reader is null
     * @since Commons IO 1.2
     */
    public static LineIterator lineIterator(Reader reader) {
        return new LineIterator(reader);
    }

    /**
     * Return an Iterator for the lines in an <code>InputStream</code>, using
     * the character encoding specified (or default encoding if null).
     * <p>
     * <code>LineIterator</code> holds a reference to the open
     * <code>InputStream</code> specified here. When you have finished with the
     * iterator you should close the stream to free internal resources. This can
     * be done by closing the stream directly, or by calling
     * {@link LineIterator#close()} or
     * {@link LineIterator#closeQuietly(LineIterator)}.
     * <p>
     * The recommended usage pattern is:
     * 
     * <pre>
     * try {
     *     LineIterator it = IOUtils.lineIterator(stream, &quot;UTF-8&quot;);
     *     while (it.hasNext()) {
     *         String line = it.nextLine();
     *         // / do something with line
     *     }
     * } finally {
     *     IOUtils.closeQuietly(stream);
     * }
     * </pre>
     * 
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @param encoding
     *            the encoding to use, null means platform default
     * @return an Iterator of the lines in the reader, never null
     * @throws IllegalArgumentException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs, such as if the encoding is invalid
     * @since Commons IO 1.2
     */
    public static LineIterator lineIterator(InputStream input, String encoding)
            throws IOException {
        Reader reader = null;
        if (encoding == null) {
            reader = new InputStreamReader(input);
        } else {
            reader = new InputStreamReader(input, encoding);
        }
        return new LineIterator(reader);
    }

    // -----------------------------------------------------------------------
    /**
     * Convert the specified CharSequence to an input stream, encoded as bytes
     * using the default character encoding of the platform.
     * 
     * @param input
     *            the CharSequence to convert
     * @return an input stream
     * @since Commons IO 2.0
     */
    public static InputStream toInputStream(CharSequence input) {
        return toInputStream(input.toString());
    }

    /**
     * Convert the specified CharSequence to an input stream, encoded as bytes
     * using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * 
     * @param input
     *            the CharSequence to convert
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws IOException
     *             if the encoding is invalid
     * @return an input stream
     * @since Commons IO 2.0
     */
    public static InputStream toInputStream(CharSequence input, String encoding)
            throws IOException {
        return toInputStream(input.toString(), encoding);
    }

    // -----------------------------------------------------------------------
    /**
     * Convert the specified string to an input stream, encoded as bytes using
     * the default character encoding of the platform.
     * 
     * @param input
     *            the string to convert
     * @return an input stream
     * @since Commons IO 1.1
     */
    public static InputStream toInputStream(String input) {
        byte[] bytes = input.getBytes();
        return new ByteArrayInputStream(bytes);
    }

    /**
     * Convert the specified string to an input stream, encoded as bytes using
     * the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * 
     * @param input
     *            the string to convert
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws IOException
     *             if the encoding is invalid
     * @return an input stream
     * @since Commons IO 1.1
     */
    public static InputStream toInputStream(String input, String encoding)
            throws IOException {
        byte[] bytes = encoding != null ? input.getBytes(encoding) : input
                .getBytes();
        return new ByteArrayInputStream(bytes);
    }

    // write byte[]
    // -----------------------------------------------------------------------
    /**
     * Writes bytes from a <code>byte[]</code> to an <code>OutputStream</code>.
     * 
     * @param data
     *            the byte array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToOutputStream(byte[] data, OutputStream output)
            throws IOException {
        if (data != null) {
            output.write(data);
        }
    }

    /**
     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
     * using the default character encoding of the platform.
     * <p>
     * This method uses {@link String#String(byte[])}.
     * 
     * @param data
     *            the byte array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToWriter(byte[] data, Writer output)
            throws IOException {
        if (data != null) {
            output.write(new String(data));
        }
    }

    /**
     * Writes bytes from a <code>byte[]</code> to chars on a <code>Writer</code>
     * using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link String#String(byte[], String)}.
     * 
     * @param data
     *            the byte array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToWriter(byte[] data, Writer output, String encoding)
            throws IOException {
        if (data != null) {
            if (encoding == null) {
                writeToWriter(data, output);
            } else {
                output.write(new String(data, encoding));
            }
        }
    }

    // write char[]
    // -----------------------------------------------------------------------
    /**
     * Writes chars from a <code>char[]</code> to a <code>Writer</code> using
     * the default character encoding of the platform.
     * 
     * @param data
     *            the char array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToWriter(char[] data, Writer output)
            throws IOException {
        if (data != null) {
            output.write(data);
        }
    }

    /**
     * Writes chars from a <code>char[]</code> to bytes on an
     * <code>OutputStream</code>.
     * <p>
     * This method uses {@link String#String(char[])} and
     * {@link String#getBytes()}.
     * 
     * @param data
     *            the char array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToOutputStream(char[] data, OutputStream output)
            throws IOException {
        if (data != null) {
            output.write(new String(data).getBytes());
        }
    }

    /**
     * Writes chars from a <code>char[]</code> to bytes on an
     * <code>OutputStream</code> using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link String#String(char[])} and
     * {@link String#getBytes(String)}.
     * 
     * @param data
     *            the char array to write, do not modify during output, null
     *            ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToOutputStream(char[] data, OutputStream output,
            String encoding) throws IOException {
        if (data != null) {
            if (encoding == null) {
                writeToOutputStream(data, output);
            } else {
                output.write(new String(data).getBytes(encoding));
            }
        }
    }

    // write CharSequence
    // -----------------------------------------------------------------------
    /**
     * Writes chars from a <code>CharSequence</code> to a <code>Writer</code>.
     * 
     * @param data
     *            the <code>CharSequence</code> to write, null ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 2.0
     */
    public static void writeToWriter(CharSequence data, Writer output)
            throws IOException {
        if (data != null) {
            writeToWriter(data.toString(), output);
        }
    }

    /**
     * Writes chars from a <code>CharSequence</code> to bytes on an
     * <code>OutputStream</code> using the default character encoding of the
     * platform.
     * <p>
     * This method uses {@link String#getBytes()}.
     * 
     * @param data
     *            the <code>CharSequence</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 2.0
     */
    public static void writeToOutputStream(CharSequence data,
            OutputStream output) throws IOException {
        if (data != null) {
            writeToOutputStream(data.toString(), output);
        }
    }

    /**
     * Writes chars from a <code>CharSequence</code> to bytes on an
     * <code>OutputStream</code> using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link String#getBytes(String)}.
     * 
     * @param data
     *            the <code>CharSequence</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 2.0
     */
    public static void writeToOutputStream(CharSequence data,
            OutputStream output, String encoding) throws IOException {
        if (data != null) {
            writeToOutputStream(data.toString(), output, encoding);
        }
    }

    // write String
    // -----------------------------------------------------------------------
    /**
     * Writes chars from a <code>String</code> to a <code>Writer</code>.
     * 
     * @param data
     *            the <code>String</code> to write, null ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToWriter(String data, Writer output)
            throws IOException {
        if (data != null) {
            output.write(data);
        }
    }

    /**
     * Writes chars from a <code>String</code> to bytes on an
     * <code>OutputStream</code> using the default character encoding of the
     * platform.
     * <p>
     * This method uses {@link String#getBytes()}.
     * 
     * @param data
     *            the <code>String</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToOutputStream(String data, OutputStream output)
            throws IOException {
        if (data != null) {
            output.write(data.getBytes());
        }
    }

    /**
     * Writes chars from a <code>String</code> to bytes on an
     * <code>OutputStream</code> using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link String#getBytes(String)}.
     * 
     * @param data
     *            the <code>String</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeToOutputStream(String data, OutputStream output,
            String encoding) throws IOException {
        if (data != null) {
            if (encoding == null) {
                writeToOutputStream(data, output);
            } else {
                output.write(data.getBytes(encoding));
            }
        }
    }

    // write StringBuffer
    // -----------------------------------------------------------------------
    /**
     * Writes chars from a <code>StringBuffer</code> to a <code>Writer</code>.
     * 
     * @param data
     *            the <code>StringBuffer</code> to write, null ignored
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     * @deprecated replaced by write(CharSequence, Writer)
     */
    @Deprecated
    public static void writeToWriter(StringBuffer data, Writer output)
            throws IOException {
        if (data != null) {
            output.write(data.toString());
        }
    }

    /**
     * Writes chars from a <code>StringBuffer</code> to bytes on an
     * <code>OutputStream</code> using the default character encoding of the
     * platform.
     * <p>
     * This method uses {@link String#getBytes()}.
     * 
     * @param data
     *            the <code>StringBuffer</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     * @deprecated replaced by write(CharSequence, OutputStream)
     */
    @Deprecated
    public static void writeToOutputStream(StringBuffer data,
            OutputStream output) throws IOException {
        if (data != null) {
            output.write(data.toString().getBytes());
        }
    }

    /**
     * Writes chars from a <code>StringBuffer</code> to bytes on an
     * <code>OutputStream</code> using the specified character encoding.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link String#getBytes(String)}.
     * 
     * @param data
     *            the <code>StringBuffer</code> to write, null ignored
     * @param output
     *            the <code>OutputStream</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     * @deprecated replaced by write(CharSequence, OutputStream, String)
     */
    @Deprecated
    public static void writeToOutputStream(StringBuffer data,
            OutputStream output, String encoding) throws IOException {
        if (data != null) {
            if (encoding == null) {
                writeToOutputStream(data, output);
            } else {
                output.write(data.toString().getBytes(encoding));
            }
        }
    }

    // writeLines
    // -----------------------------------------------------------------------
    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * an <code>OutputStream</code> line by line, using the default character
     * encoding of the platform and the specified line ending.
     * 
     * @param lines
     *            the lines to write, null entries produce blank lines
     * @param lineEnding
     *            the line separator to use, null is system default
     * @param output
     *            the <code>OutputStream</code> to write to, not null, not
     *            closed
     * @throws NullPointerException
     *             if the output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeLines(Collection<?> lines, String lineEnding,
            OutputStream output) throws IOException {
        if (lines == null) {
            return;
        }
        if (lineEnding == null) {
            lineEnding = LINE_SEPARATOR;
        }
        for (Object line : lines) {
            if (line != null) {
                output.write(line.toString().getBytes());
            }
            output.write(lineEnding.getBytes());
        }
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * an <code>OutputStream</code> line by line, using the specified character
     * encoding and the specified line ending.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * 
     * @param lines
     *            the lines to write, null entries produce blank lines
     * @param lineEnding
     *            the line separator to use, null is system default
     * @param output
     *            the <code>OutputStream</code> to write to, not null, not
     *            closed
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if the output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeLines(Collection<?> lines, String lineEnding,
            OutputStream output, String encoding) throws IOException {
        if (encoding == null) {
            writeLines(lines, lineEnding, output);
        } else {
            if (lines == null) {
                return;
            }
            if (lineEnding == null) {
                lineEnding = LINE_SEPARATOR;
            }
            for (Object line : lines) {
                if (line != null) {
                    output.write(line.toString().getBytes(encoding));
                }
                output.write(lineEnding.getBytes(encoding));
            }
        }
    }

    /**
     * Writes the <code>toString()</code> value of each item in a collection to
     * a <code>Writer</code> line by line, using the specified line ending.
     * 
     * @param lines
     *            the lines to write, null entries produce blank lines
     * @param lineEnding
     *            the line separator to use, null is system default
     * @param writer
     *            the <code>Writer</code> to write to, not null, not closed
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void writeLines(Collection<?> lines, String lineEnding,
            Writer writer) throws IOException {
        if (lines == null) {
            return;
        }
        if (lineEnding == null) {
            lineEnding = LINE_SEPARATOR;
        }
        for (Object line : lines) {
            if (line != null) {
                writer.write(line.toString());
            }
            writer.write(lineEnding);
        }
    }

    // copy from InputStream
    // -----------------------------------------------------------------------
    /**
     * Copy bytes from an <code>InputStream</code> to an
     * <code>OutputStream</code>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * <p>
     * Large streams (over 2GB) will return a bytes copied value of
     * <code>-1</code> after the copy has completed since the correct number of
     * bytes cannot be returned as an int. For large streams use the
     * <code>copyLarge(InputStream, OutputStream)</code> method.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @param output
     *            the <code>OutputStream</code> to write to
     * @return the number of bytes copied, or -1 if &gt; Integer.MAX_VALUE
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static int copy(InputStream input, OutputStream output)
            throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    /**
     * Copy bytes from a large (over 2GB) <code>InputStream</code> to an
     * <code>OutputStream</code>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @param output
     *            the <code>OutputStream</code> to write to
     * @return the number of bytes copied
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.3
     */
    public static long copyLarge(InputStream input, OutputStream output)
            throws IOException {
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    /**
     * Copy bytes from an <code>InputStream</code> to chars on a
     * <code>Writer</code> using the default character encoding of the platform.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * <p>
     * This method uses {@link InputStreamReader}.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @param output
     *            the <code>Writer</code> to write to
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void copy(InputStream input, Writer output)
            throws IOException {
        InputStreamReader in = new InputStreamReader(input);
        copy(in, output);
    }

    /**
     * Copy bytes from an <code>InputStream</code> to chars on a
     * <code>Writer</code> using the specified character encoding.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedInputStream</code>.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method uses {@link InputStreamReader}.
     * 
     * @param input
     *            the <code>InputStream</code> to read from
     * @param output
     *            the <code>Writer</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void copy(InputStream input, Writer output, String encoding)
            throws IOException {
        if (encoding == null) {
            copy(input, output);
        } else {
            InputStreamReader in = new InputStreamReader(input, encoding);
            copy(in, output);
        }
    }

    // copy from Reader
    // -----------------------------------------------------------------------
    /**
     * Copy chars from a <code>Reader</code> to a <code>Writer</code>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * <p>
     * Large streams (over 2GB) will return a chars copied value of
     * <code>-1</code> after the copy has completed since the correct number of
     * chars cannot be returned as an int. For large streams use the
     * <code>copyLarge(Reader, Writer)</code> method.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @param output
     *            the <code>Writer</code> to write to
     * @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static int copy(Reader input, Writer output) throws IOException {
        long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    /**
     * Copy chars from a large (over 2GB) <code>Reader</code> to a
     * <code>Writer</code>.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @param output
     *            the <code>Writer</code> to write to
     * @return the number of characters copied
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.3
     */
    public static long copyLarge(Reader input, Writer output)
            throws IOException {
        char[] buffer = new char[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

    /**
     * Copy chars from a <code>Reader</code> to bytes on an
     * <code>OutputStream</code> using the default character encoding of the
     * platform, and calling flush.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * <p>
     * Due to the implementation of OutputStreamWriter, this method performs a
     * flush.
     * <p>
     * This method uses {@link OutputStreamWriter}.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @param output
     *            the <code>OutputStream</code> to write to
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void copy(Reader input, OutputStream output)
            throws IOException {
        OutputStreamWriter out = new OutputStreamWriter(output);
        copy(input, out);
        // XXX Unless anyone is planning on rewriting OutputStreamWriter, we
        // have to flush here.
        out.flush();
    }

    /**
     * Copy chars from a <code>Reader</code> to bytes on an
     * <code>OutputStream</code> using the specified character encoding, and
     * calling flush.
     * <p>
     * This method buffers the input internally, so there is no need to use a
     * <code>BufferedReader</code>.
     * <p>
     * Character encoding names can be found at <a
     * href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * Due to the implementation of OutputStreamWriter, this method performs a
     * flush.
     * <p>
     * This method uses {@link OutputStreamWriter}.
     * 
     * @param input
     *            the <code>Reader</code> to read from
     * @param output
     *            the <code>OutputStream</code> to write to
     * @param encoding
     *            the encoding to use, null means platform default
     * @throws NullPointerException
     *             if the input or output is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static void copy(Reader input, OutputStream output, String encoding)
            throws IOException {
        if (encoding == null) {
            copy(input, output);
        } else {
            OutputStreamWriter out = new OutputStreamWriter(output, encoding);
            copy(input, out);
            // XXX Unless anyone is planning on rewriting OutputStreamWriter,
            // we have to flush here.
            out.flush();
        }
    }

    // content equals
    // -----------------------------------------------------------------------
    /**
     * Compare the contents of two Streams to determine if they are equal or
     * not.
     * <p>
     * This method buffers the input internally using
     * <code>BufferedInputStream</code> if they are not already buffered.
     * 
     * @param input1
     *            the first stream
     * @param input2
     *            the second stream
     * @return true if the content of the streams are equal or they both don't
     *         exist, false otherwise
     * @throws NullPointerException
     *             if either input is null
     * @throws IOException
     *             if an I/O error occurs
     */
    public static boolean contentEquals(InputStream input1, InputStream input2)
            throws IOException {
        if (!(input1 instanceof BufferedInputStream)) {
            input1 = new BufferedInputStream(input1);
        }
        if (!(input2 instanceof BufferedInputStream)) {
            input2 = new BufferedInputStream(input2);
        }

        int ch = input1.read();
        while (-1 != ch) {
            int ch2 = input2.read();
            if (ch != ch2) {
                return false;
            }
            ch = input1.read();
        }

        int ch2 = input2.read();
        return (ch2 == -1);
    }

    /**
     * Compare the contents of two Readers to determine if they are equal or
     * not.
     * <p>
     * This method buffers the input internally using
     * <code>BufferedReader</code> if they are not already buffered.
     * 
     * @param input1
     *            the first reader
     * @param input2
     *            the second reader
     * @return true if the content of the readers are equal or they both don't
     *         exist, false otherwise
     * @throws NullPointerException
     *             if either input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since Commons IO 1.1
     */
    public static boolean contentEquals(Reader input1, Reader input2)
            throws IOException {
        if (!(input1 instanceof BufferedReader)) {
            input1 = new BufferedReader(input1);
        }
        if (!(input2 instanceof BufferedReader)) {
            input2 = new BufferedReader(input2);
        }

        int ch = input1.read();
        while (-1 != ch) {
            int ch2 = input2.read();
            if (ch != ch2) {
                return false;
            }
            ch = input1.read();
        }

        int ch2 = input2.read();
        return (ch2 == -1);
    }

    /**
     * Skip bytes from an input byte stream. This implementation guarantees that
     * it will read as many bytes as possible before giving up; this may not
     * always be the case for subclasses of {@link Reader}.
     * 
     * @param input
     *            byte stream to skip
     * @param toSkip
     *            number of bytes to skip.
     * @return number of bytes actually skipped.
     * 
     * @see InputStream#skip(long)
     * 
     * @throws IOException
     *             if there is a problem reading the file
     * @throws IllegalArgumentException
     *             if toSkip is negative
     * @since Commons IO 2.0
     */
    public static long skip(InputStream input, long toSkip) throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException(
                    "Skip count must be non-negative, actual: " + toSkip);
        }
        /*
         * N.B. no need to synchronize this because: - we don't care if the
         * buffer is created multiple times (the data is ignored) - we always
         * use the same size buffer, so if it it is recreated it will still be
         * OK (if the buffer size were variable, we would need to synch. to
         * ensure some other thread did not create a smaller one)
         */
        if (SKIP_BYTE_BUFFER == null) {
            SKIP_BYTE_BUFFER = new byte[SKIP_BUFFER_SIZE];
        }
        long remain = toSkip;
        while (remain > 0) {
            long n = input.read(SKIP_BYTE_BUFFER, 0,
                    (int) Math.min(remain, SKIP_BUFFER_SIZE));
            if (n < 0) { // EOF
                break;
            }
            remain -= n;
        }
        return toSkip - remain;
    }

    /**
     * Skip characters from an input character stream. This implementation
     * guarantees that it will read as many characters as possible before giving
     * up; this may not always be the case for subclasses of {@link Reader}.
     * 
     * @param input
     *            character stream to skip
     * @param toSkip
     *            number of characters to skip.
     * @return number of characters actually skipped.
     * 
     * @see Reader#skip(long)
     * 
     * @throws IOException
     *             if there is a problem reading the file
     * @throws IllegalArgumentException
     *             if toSkip is negative
     * @since Commons IO 2.0
     */
    public static long skip(Reader input, long toSkip) throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException(
                    "Skip count must be non-negative, actual: " + toSkip);
        }
        /*
         * N.B. no need to synchronize this because: - we don't care if the
         * buffer is created multiple times (the data is ignored) - we always
         * use the same size buffer, so if it it is recreated it will still be
         * OK (if the buffer size were variable, we would need to synch. to
         * ensure some other thread did not create a smaller one)
         */
        if (SKIP_CHAR_BUFFER == null) {
            SKIP_CHAR_BUFFER = new char[SKIP_BUFFER_SIZE];
        }
        long remain = toSkip;
        while (remain > 0) {
            long n = input.read(SKIP_CHAR_BUFFER, 0,
                    (int) Math.min(remain, SKIP_BUFFER_SIZE));
            if (n < 0) { // EOF
                break;
            }
            remain -= n;
        }
        return toSkip - remain;
    }

    /**
     * Skip the requested number of bytes or fail if there are not enough left.
     * <p>
     * This allows for the possibility that {@link InputStream#skip(long)} may
     * not skip as many bytes as requested (most likely because of reaching
     * EOF).
     * 
     * @param input
     *            stream to skip
     * @param toSkip
     *            the number of bytes to skip
     * @see InputStream#skip(long)
     * 
     * @throws IOException
     *             if there is a problem reading the file
     * @throws IllegalArgumentException
     *             if toSkip is negative
     * @throws EOFException
     *             if the number of bytes skipped was incorrect
     * @since Commons IO 2.0
     */
    public static void skipFully(InputStream input, long toSkip)
            throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException(
                    "Bytes to skip must not be negative: " + toSkip);
        }
        long skipped = skip(input, toSkip);
        if (skipped != toSkip) {
            throw new EOFException("Bytes to skip: " + toSkip + " actual: "
                    + skipped);
        }
    }

    /**
     * Skip the requested number of characters or fail if there are not enough
     * left.
     * <p>
     * This allows for the possibility that {@link Reader#skip(long)} may not
     * skip as many characters as requested (most likely because of reaching
     * EOF).
     * 
     * @param input
     *            stream to skip
     * @param toSkip
     *            the number of characters to skip
     * @see Reader#skip(long)
     * 
     * @throws IOException
     *             if there is a problem reading the file
     * @throws IllegalArgumentException
     *             if toSkip is negative
     * @throws EOFException
     *             if the number of characters skipped was incorrect
     * @since Commons IO 2.0
     */
    public static void skipFully(Reader input, long toSkip) throws IOException {
        long skipped = skip(input, toSkip);
        if (skipped != toSkip) {
            throw new EOFException("Bytes to skip: " + toSkip + " actual: "
                    + skipped);
        }
    }

    // ///////////////////////////////////////////
    //
    // EndianUtils
    //
    // ///////////////////////////////////////////

    /**
     * Converts a "short" value between endian systems.
     * 
     * @param value
     *            value to convert
     * @return the converted value
     */
    public static short swapShort(short value) {
        return (short) ((((value >> 0) & 0xff) << 8) + (((value >> 8) & 0xff) << 0));
    }

    /**
     * Converts a "int" value between endian systems.
     * 
     * @param value
     *            value to convert
     * @return the converted value
     */
    public static int swapInteger(int value) {
        return (((value >> 0) & 0xff) << 24) + (((value >> 8) & 0xff) << 16)
                + (((value >> 16) & 0xff) << 8) + (((value >> 24) & 0xff) << 0);
    }

    /**
     * Converts a "long" value between endian systems.
     * 
     * @param value
     *            value to convert
     * @return the converted value
     */
    public static long swapLong(long value) {
        return (((value >> 0) & 0xff) << 56) + (((value >> 8) & 0xff) << 48)
                + (((value >> 16) & 0xff) << 40)
                + (((value >> 24) & 0xff) << 32)
                + (((value >> 32) & 0xff) << 24)
                + (((value >> 40) & 0xff) << 16)
                + (((value >> 48) & 0xff) << 8) + (((value >> 56) & 0xff) << 0);
    }

    /**
     * Converts a "float" value between endian systems.
     * 
     * @param value
     *            value to convert
     * @return the converted value
     */
    public static float swapFloat(float value) {
        return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value)));
    }

    /**
     * Converts a "double" value between endian systems.
     * 
     * @param value
     *            value to convert
     * @return the converted value
     */
    public static double swapDouble(double value) {
        return Double
                .longBitsToDouble(swapLong(Double.doubleToLongBits(value)));
    }

    // ========================================== Swapping read/write routines

    /**
     * Writes a "short" value to a byte array at a given offset. The value is
     * converted to the opposed endian system while writing.
     * 
     * @param data
     *            target byte array
     * @param offset
     *            starting offset in the byte array
     * @param value
     *            value to write
     */
    public static void writeSwappedShort(byte[] data, int offset, short value) {
        data[offset + 0] = (byte) ((value >> 0) & 0xff);
        data[offset + 1] = (byte) ((value >> 8) & 0xff);
    }

    /**
     * Reads a "short" value from a byte array at a given offset. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static short readSwappedShort(byte[] data, int offset) {
        return (short) (((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8));
    }

    /**
     * Reads an unsigned short (16-bit) value from a byte array at a given
     * offset. The value is converted to the opposed endian system while
     * reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static int readSwappedUnsignedShort(byte[] data, int offset) {
        return (((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8));
    }

    /**
     * Writes a "int" value to a byte array at a given offset. The value is
     * converted to the opposed endian system while writing.
     * 
     * @param data
     *            target byte array
     * @param offset
     *            starting offset in the byte array
     * @param value
     *            value to write
     */
    public static void writeSwappedInteger(byte[] data, int offset, int value) {
        data[offset + 0] = (byte) ((value >> 0) & 0xff);
        data[offset + 1] = (byte) ((value >> 8) & 0xff);
        data[offset + 2] = (byte) ((value >> 16) & 0xff);
        data[offset + 3] = (byte) ((value >> 24) & 0xff);
    }

    /**
     * Reads a "int" value from a byte array at a given offset. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static int readSwappedInteger(byte[] data, int offset) {
        return (((data[offset + 0] & 0xff) << 0)
                + ((data[offset + 1] & 0xff) << 8)
                + ((data[offset + 2] & 0xff) << 16) + ((data[offset + 3] & 0xff) << 24));
    }

    /**
     * Reads an unsigned integer (32-bit) value from a byte array at a given
     * offset. The value is converted to the opposed endian system while
     * reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static long readSwappedUnsignedInteger(byte[] data, int offset) {
        long low = (((data[offset + 0] & 0xff) << 0)
                + ((data[offset + 1] & 0xff) << 8) + ((data[offset + 2] & 0xff) << 16));

        long high = data[offset + 3] & 0xff;

        return (high << 24) + (0xffffffffL & low);
    }

    /**
     * Writes a "long" value to a byte array at a given offset. The value is
     * converted to the opposed endian system while writing.
     * 
     * @param data
     *            target byte array
     * @param offset
     *            starting offset in the byte array
     * @param value
     *            value to write
     */
    public static void writeSwappedLong(byte[] data, int offset, long value) {
        data[offset + 0] = (byte) ((value >> 0) & 0xff);
        data[offset + 1] = (byte) ((value >> 8) & 0xff);
        data[offset + 2] = (byte) ((value >> 16) & 0xff);
        data[offset + 3] = (byte) ((value >> 24) & 0xff);
        data[offset + 4] = (byte) ((value >> 32) & 0xff);
        data[offset + 5] = (byte) ((value >> 40) & 0xff);
        data[offset + 6] = (byte) ((value >> 48) & 0xff);
        data[offset + 7] = (byte) ((value >> 56) & 0xff);
    }

    /**
     * Reads a "long" value from a byte array at a given offset. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static long readSwappedLong(byte[] data, int offset) {
        long low = ((data[offset + 0] & 0xff) << 0)
                + ((data[offset + 1] & 0xff) << 8)
                + ((data[offset + 2] & 0xff) << 16)
                + ((data[offset + 3] & 0xff) << 24);
        long high = ((data[offset + 4] & 0xff) << 0)
                + ((data[offset + 5] & 0xff) << 8)
                + ((data[offset + 6] & 0xff) << 16)
                + ((data[offset + 7] & 0xff) << 24);
        return (high << 32) + (0xffffffffL & low);
    }

    /**
     * Writes a "float" value to a byte array at a given offset. The value is
     * converted to the opposed endian system while writing.
     * 
     * @param data
     *            target byte array
     * @param offset
     *            starting offset in the byte array
     * @param value
     *            value to write
     */
    public static void writeSwappedFloat(byte[] data, int offset, float value) {
        writeSwappedInteger(data, offset, Float.floatToIntBits(value));
    }

    /**
     * Reads a "float" value from a byte array at a given offset. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static float readSwappedFloat(byte[] data, int offset) {
        return Float.intBitsToFloat(readSwappedInteger(data, offset));
    }

    /**
     * Writes a "double" value to a byte array at a given offset. The value is
     * converted to the opposed endian system while writing.
     * 
     * @param data
     *            target byte array
     * @param offset
     *            starting offset in the byte array
     * @param value
     *            value to write
     */
    public static void writeSwappedDouble(byte[] data, int offset, double value) {
        writeSwappedLong(data, offset, Double.doubleToLongBits(value));
    }

    /**
     * Reads a "double" value from a byte array at a given offset. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param data
     *            source byte array
     * @param offset
     *            starting offset in the byte array
     * @return the value read
     */
    public static double readSwappedDouble(byte[] data, int offset) {
        return Double.longBitsToDouble(readSwappedLong(data, offset));
    }

    /**
     * Writes a "short" value to an OutputStream. The value is converted to the
     * opposed endian system while writing.
     * 
     * @param output
     *            target OutputStream
     * @param value
     *            value to write
     * @throws IOException
     *             in case of an I/O problem
     */
    public static void writeSwappedShort(OutputStream output, short value)
            throws IOException {
        output.write((byte) ((value >> 0) & 0xff));
        output.write((byte) ((value >> 8) & 0xff));
    }

    /**
     * Reads a "short" value from an InputStream. The value is converted to the
     * opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static short readSwappedShort(InputStream input) throws IOException {
        return (short) (((readInternal(input) & 0xff) << 0) + ((readInternal(input) & 0xff) << 8));
    }

    /**
     * Reads a unsigned short (16-bit) from an InputStream. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static int readSwappedUnsignedShort(InputStream input)
            throws IOException {
        int value1 = readInternal(input);
        int value2 = readInternal(input);

        return (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8));
    }

    /**
     * Writes a "int" value to an OutputStream. The value is converted to the
     * opposed endian system while writing.
     * 
     * @param output
     *            target OutputStream
     * @param value
     *            value to write
     * @throws IOException
     *             in case of an I/O problem
     */
    public static void writeSwappedInteger(OutputStream output, int value)
            throws IOException {
        output.write((byte) ((value >> 0) & 0xff));
        output.write((byte) ((value >> 8) & 0xff));
        output.write((byte) ((value >> 16) & 0xff));
        output.write((byte) ((value >> 24) & 0xff));
    }

    /**
     * Reads a "int" value from an InputStream. The value is converted to the
     * opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static int readSwappedInteger(InputStream input) throws IOException {
        int value1 = readInternal(input);
        int value2 = readInternal(input);
        int value3 = readInternal(input);
        int value4 = readInternal(input);

        return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8)
                + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24);
    }

    /**
     * Reads a unsigned integer (32-bit) from an InputStream. The value is
     * converted to the opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static long readSwappedUnsignedInteger(InputStream input)
            throws IOException {
        int value1 = readInternal(input);
        int value2 = readInternal(input);
        int value3 = readInternal(input);
        int value4 = readInternal(input);

        long low = (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16));

        long high = value4 & 0xff;

        return (high << 24) + (0xffffffffL & low);
    }

    /**
     * Writes a "long" value to an OutputStream. The value is converted to the
     * opposed endian system while writing.
     * 
     * @param output
     *            target OutputStream
     * @param value
     *            value to write
     * @throws IOException
     *             in case of an I/O problem
     */
    public static void writeSwappedLong(OutputStream output, long value)
            throws IOException {
        output.write((byte) ((value >> 0) & 0xff));
        output.write((byte) ((value >> 8) & 0xff));
        output.write((byte) ((value >> 16) & 0xff));
        output.write((byte) ((value >> 24) & 0xff));
        output.write((byte) ((value >> 32) & 0xff));
        output.write((byte) ((value >> 40) & 0xff));
        output.write((byte) ((value >> 48) & 0xff));
        output.write((byte) ((value >> 56) & 0xff));
    }

    /**
     * Reads a "long" value from an InputStream. The value is converted to the
     * opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static long readSwappedLong(InputStream input) throws IOException {
        byte[] bytes = new byte[8];
        for (int i = 0; i < 8; i++) {
            bytes[i] = (byte) readInternal(input);
        }
        return readSwappedLong(bytes, 0);
    }

    /**
     * Writes a "float" value to an OutputStream. The value is converted to the
     * opposed endian system while writing.
     * 
     * @param output
     *            target OutputStream
     * @param value
     *            value to write
     * @throws IOException
     *             in case of an I/O problem
     */
    public static void writeSwappedFloat(OutputStream output, float value)
            throws IOException {
        writeSwappedInteger(output, Float.floatToIntBits(value));
    }

    /**
     * Reads a "float" value from an InputStream. The value is converted to the
     * opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static float readSwappedFloat(InputStream input) throws IOException {
        return Float.intBitsToFloat(readSwappedInteger(input));
    }

    /**
     * Writes a "double" value to an OutputStream. The value is converted to the
     * opposed endian system while writing.
     * 
     * @param output
     *            target OutputStream
     * @param value
     *            value to write
     * @throws IOException
     *             in case of an I/O problem
     */
    public static void writeSwappedDouble(OutputStream output, double value)
            throws IOException {
        writeSwappedLong(output, Double.doubleToLongBits(value));
    }

    /**
     * Reads a "double" value from an InputStream. The value is converted to the
     * opposed endian system while reading.
     * 
     * @param input
     *            source InputStream
     * @return the value just read
     * @throws IOException
     *             in case of an I/O problem
     */
    public static double readSwappedDouble(InputStream input)
            throws IOException {
        return Double.longBitsToDouble(readSwappedLong(input));
    }

    /**
     * Reads the next byte from the input stream.
     * 
     * @param input
     *            the stream
     * @return the byte
     * @throws IOException
     *             if the end of file is reached
     */
    private static int readInternal(InputStream input) throws IOException {
        int value = input.read();

        if (-1 == value) {
            throw new EOFException("Unexpected EOF reached");
        }

        return value;
    }

    // ///////////////////////////////////////////
    //
    // FileSystemUtils
    //
    // ///////////////////////////////////////////

    /** Operating system state flag for error. */
    private static final int INIT_PROBLEM = -1;
    /** Operating system state flag for neither Unix nor Windows. */
    private static final int OTHER = 0;
    /** Operating system state flag for Windows. */
    private static final int WINDOWS = 1;
    /** Operating system state flag for Unix. */
    private static final int UNIX = 2;
    /** Operating system state flag for Posix flavour Unix. */
    private static final int POSIX_UNIX = 3;

    /** The operating system flag. */
    private static final int OS;

    /** The path to df */
    private static final String DF;

    static {
        int os = OTHER;
        String dfPath = "df";
        try {
            String osName = System.getProperty("os.name");
            if (osName == null) {
                throw new IOException("os.name not found");
            }
            osName = osName.toLowerCase(Locale.ENGLISH);
            // match
            if (osName.indexOf("windows") != -1) {
                os = WINDOWS;
            } else if (osName.indexOf("linux") != -1
                    || osName.indexOf("mpe/ix") != -1
                    || osName.indexOf("freebsd") != -1
                    || osName.indexOf("irix") != -1
                    || osName.indexOf("digital unix") != -1
                    || osName.indexOf("unix") != -1
                    || osName.indexOf("mac os x") != -1) {
                os = UNIX;
            } else if (osName.indexOf("sun os") != -1
                    || osName.indexOf("sunos") != -1
                    || osName.indexOf("solaris") != -1) {
                os = POSIX_UNIX;
                dfPath = "/usr/xpg4/bin/df";
            } else if (osName.indexOf("hp-ux") != -1
                    || osName.indexOf("aix") != -1) {
                os = POSIX_UNIX;
            } else {
                os = OTHER;
            }

        } catch (Exception ex) {
            os = INIT_PROBLEM;
        }
        OS = os;
        DF = dfPath;
    }

    /**
     * Returns the free space on a drive or volume in kilobytes by invoking the
     * command line.
     * 
     * <pre>
     * FileSystemUtils.freeSpaceKb(&quot;C:&quot;); // Windows
     * FileSystemUtils.freeSpaceKb(&quot;/volume&quot;); // *nix
     * </pre>
     * 
     * The free space is calculated via the command line. It uses 'dir /-c' on
     * Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
     * <p>
     * In order to work, you must be running Windows, or have a implementation
     * of Unix df that supports GNU format when passed -k (or -kP). If you are
     * going to rely on this code, please check that it works on your OS by
     * running some simple tests to compare the command line with the output
     * from this class. If your operating system isn't supported, please raise a
     * JIRA call detailing the exact result from df -k and as much other detail
     * as possible, thanks.
     * 
     * @param path
     *            the path to get free space for, not null, not empty on Unix
     * @return the amount of free drive space on the drive or volume in
     *         kilobytes
     * @throws IllegalArgumentException
     *             if the path is invalid
     * @throws IllegalStateException
     *             if an error occurred in initialisation
     * @throws IOException
     *             if an error occurs when finding the free space
     * @since Commons IO 1.2, enhanced OS support in 1.3
     */
    public static long freeSpaceKb(String path) throws IOException {
        return freeSpaceKb(path, -1);
    }

    /**
     * Returns the free space on a drive or volume in kilobytes by invoking the
     * command line.
     * 
     * <pre>
     * FileSystemUtils.freeSpaceKb(&quot;C:&quot;); // Windows
     * FileSystemUtils.freeSpaceKb(&quot;/volume&quot;); // *nix
     * </pre>
     * 
     * The free space is calculated via the command line. It uses 'dir /-c' on
     * Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
     * <p>
     * In order to work, you must be running Windows, or have a implementation
     * of Unix df that supports GNU format when passed -k (or -kP). If you are
     * going to rely on this code, please check that it works on your OS by
     * running some simple tests to compare the command line with the output
     * from this class. If your operating system isn't supported, please raise a
     * JIRA call detailing the exact result from df -k and as much other detail
     * as possible, thanks.
     * 
     * @param path
     *            the path to get free space for, not null, not empty on Unix
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the amount of free drive space on the drive or volume in
     *         kilobytes
     * @throws IllegalArgumentException
     *             if the path is invalid
     * @throws IllegalStateException
     *             if an error occurred in initialisation
     * @throws IOException
     *             if an error occurs when finding the free space
     * @since Commons IO 2.0
     */
    public static long freeSpaceKb(String path, long timeout)
            throws IOException {
        return freeSpaceOS(path, OS, true, timeout);
    }

    /**
     * Returns the disk size of the volume which holds the working directory.
     * <p>
     * Identical to:
     * 
     * <pre>
     * freeSpaceKb(new File(&quot;.&quot;).getAbsolutePath())
     * </pre>
     * 
     * @return the amount of free drive space on the drive or volume in
     *         kilobytes
     * @throws IllegalStateException
     *             if an error occurred in initialisation
     * @throws IOException
     *             if an error occurs when finding the free space
     * @since Commons IO 2.0
     */
    public static long freeSpaceKb() throws IOException {
        return freeSpaceKb(-1);
    }

    /**
     * Returns the disk size of the volume which holds the working directory.
     * <p>
     * Identical to:
     * 
     * <pre>
     * freeSpaceKb(new File(&quot;.&quot;).getAbsolutePath())
     * </pre>
     * 
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the amount of free drive space on the drive or volume in
     *         kilobytes
     * @throws IllegalStateException
     *             if an error occurred in initialisation
     * @throws IOException
     *             if an error occurs when finding the free space
     * @since Commons IO 2.0
     */
    public static long freeSpaceKb(long timeout) throws IOException {
        return freeSpaceKb(new File(".").getAbsolutePath(), timeout);
    }

    // -----------------------------------------------------------------------
    /**
     * Returns the free space on a drive or volume in a cross-platform manner.
     * Note that some OS's are NOT currently supported, including OS/390.
     * 
     * <pre>
     * FileSystemUtils.freeSpace(&quot;C:&quot;); // Windows
     * FileSystemUtils.freeSpace(&quot;/volume&quot;); // *nix
     * </pre>
     * 
     * The free space is calculated via the command line. It uses 'dir /-c' on
     * Windows and 'df' on *nix.
     * 
     * @param path
     *            the path to get free space for, not null, not empty on Unix
     * @param os
     *            the operating system code
     * @param kb
     *            whether to normalize to kilobytes
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the amount of free drive space on the drive or volume
     * @throws IllegalArgumentException
     *             if the path is invalid
     * @throws IllegalStateException
     *             if an error occurred in initialisation
     * @throws IOException
     *             if an error occurs when finding the free space
     */
    static long freeSpaceOS(String path, int os, boolean kb, long timeout)
            throws IOException {
        if (path == null) {
            throw new IllegalArgumentException("Path must not be empty");
        }
        switch (os) {
            case WINDOWS:
                return (kb ? freeSpaceWindows(path, timeout) / 1024
                        : freeSpaceWindows(path, timeout));
            case UNIX:
                return freeSpaceUnix(path, kb, false, timeout);
            case POSIX_UNIX:
                return freeSpaceUnix(path, kb, true, timeout);
            case OTHER:
                throw new IllegalStateException("Unsupported operating system");
            default:
                throw new IllegalStateException(
                        "Exception caught when determining operating system");
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Find free space on the Windows platform using the 'dir' command.
     * 
     * @param path
     *            the path to get free space for, including the colon
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the amount of free drive space on the drive
     * @throws IOException
     *             if an error occurs
     */
    static long freeSpaceWindows(String path, long timeout) throws IOException {
        path = normalizePath(path, false);
        if (path.length() > 0 && path.charAt(0) != '"') {
            path = "\"" + path + "\"";
        }

        // build and run the 'dir' command
        String[] cmdAttribs = new String[] { "cmd.exe", "/C", "dir /-c " + path };

        // read in the output of the command to an ArrayList
        List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE,
                timeout);

        // now iterate over the lines we just read and find the LAST
        // non-empty line (the free space bytes should be in the last element
        // of the ArrayList anyway, but this will ensure it works even if it's
        // not, still assuming it is on the last non-blank line)
        for (int i = lines.size() - 1; i >= 0; i--) {
            String line = lines.get(i);
            if (line.length() > 0) {
                return parseDir(line, path);
            }
        }
        // all lines are blank
        throw new IOException("Command line 'dir /-c' did not return any info "
                + "for path '" + path + "'");
    }

    /**
     * Parses the Windows dir response last line
     * 
     * @param line
     *            the line to parse
     * @param path
     *            the path that was sent
     * @return the number of bytes
     * @throws IOException
     *             if an error occurs
     */
    static long parseDir(String line, String path) throws IOException {
        // read from the end of the line to find the last numeric
        // character on the line, then continue until we find the first
        // non-numeric character, and everything between that and the last
        // numeric character inclusive is our free space bytes count
        int bytesStart = 0;
        int bytesEnd = 0;
        int j = line.length() - 1;
        innerLoop1: while (j >= 0) {
            char c = line.charAt(j);
            if (Character.isDigit(c)) {
                // found the last numeric character, this is the end of
                // the free space bytes count
                bytesEnd = j + 1;
                break innerLoop1;
            }
            j--;
        }
        innerLoop2: while (j >= 0) {
            char c = line.charAt(j);
            if (!Character.isDigit(c) && c != ',' && c != '.') {
                // found the next non-numeric character, this is the
                // beginning of the free space bytes count
                bytesStart = j + 1;
                break innerLoop2;
            }
            j--;
        }
        if (j < 0) {
            throw new IOException(
                    "Command line 'dir /-c' did not return valid info "
                            + "for path '" + path + "'");
        }

        // remove commas and dots in the bytes count
        StringBuilder buf = new StringBuilder(line.substring(bytesStart,
                bytesEnd));
        for (int k = 0; k < buf.length(); k++) {
            if (buf.charAt(k) == ',' || buf.charAt(k) == '.') {
                buf.deleteCharAt(k--);
            }
        }
        return parseBytes(buf.toString(), path);
    }

    // -----------------------------------------------------------------------
    /**
     * Find free space on the *nix platform using the 'df' command.
     * 
     * @param path
     *            the path to get free space for
     * @param kb
     *            whether to normalize to kilobytes
     * @param posix
     *            whether to use the posix standard format flag
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the amount of free drive space on the volume
     * @throws IOException
     *             if an error occurs
     */
    static long freeSpaceUnix(String path, boolean kb, boolean posix,
            long timeout) throws IOException {
        if (path.length() == 0) {
            throw new IllegalArgumentException("Path must not be empty");
        }

        // build and run the 'dir' command
        String flags = "-";
        if (kb) {
            flags += "k";
        }
        if (posix) {
            flags += "P";
        }
        String[] cmdAttribs = (flags.length() > 1 ? new String[] { DF, flags,
                path } : new String[] { DF, path });

        // perform the command, asking for up to 3 lines (header, interesting,
        // overflow)
        List<String> lines = performCommand(cmdAttribs, 3, timeout);
        if (lines.size() < 2) {
            // unknown problem, throw exception
            throw new IOException("Command line '" + DF
                    + "' did not return info as expected " + "for path '"
                    + path + "'- response was " + lines);
        }
        String line2 = lines.get(1); // the line we're interested in

        // Now, we tokenize the string. The fourth element is what we want.
        StringTokenizer tok = new StringTokenizer(line2, " ");
        if (tok.countTokens() < 4) {
            // could be long Filesystem, thus data on third line
            if (tok.countTokens() == 1 && lines.size() >= 3) {
                String line3 = lines.get(2); // the line may be interested in
                tok = new StringTokenizer(line3, " ");
            } else {
                throw new IOException("Command line '" + DF
                        + "' did not return data as expected " + "for path '"
                        + path + "'- check path is valid");
            }
        } else {
            tok.nextToken(); // Ignore Filesystem
        }
        tok.nextToken(); // Ignore 1K-blocks
        tok.nextToken(); // Ignore Used
        String freeSpace = tok.nextToken();
        return parseBytes(freeSpace, path);
    }

    // -----------------------------------------------------------------------
    /**
     * Parses the bytes from a string.
     * 
     * @param freeSpace
     *            the free space string
     * @param path
     *            the path
     * @return the number of bytes
     * @throws IOException
     *             if an error occurs
     */
    static long parseBytes(String freeSpace, String path) throws IOException {
        try {
            long bytes = Long.parseLong(freeSpace);
            if (bytes < 0) {
                throw new IOException("Command line '" + DF
                        + "' did not find free space in response "
                        + "for path '" + path + "'- check path is valid");
            }
            return bytes;

        } catch (NumberFormatException ex) {
            throw new IOExceptionWithCause("Command line '" + DF
                    + "' did not return numeric data as expected "
                    + "for path '" + path + "'- check path is valid", ex);
        }
    }

    // -----------------------------------------------------------------------
    /**
     * Performs the os command.
     * 
     * @param cmdAttribs
     *            the command line parameters
     * @param max
     *            The maximum limit for the lines returned
     * @param timeout
     *            The timout amount in milliseconds or no timeout if the value
     *            is zero or less
     * @return the parsed data
     * @throws IOException
     *             if an error occurs
     */
    static List<String> performCommand(String[] cmdAttribs, int max,
            long timeout) throws IOException {
        // this method does what it can to avoid the 'Too many open files' error
        // based on trial and error and these links:
        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4801027
        // http://forum.java.sun.com/thread.jspa?threadID=533029&messageID=2572018
        // however, its still not perfect as the JDK support is so poor
        // (see commond-exec or ant for a better multi-threaded multi-os
        // solution)

        List<String> lines = new ArrayList<String>(20);
        Process proc = null;
        InputStream in = null;
        OutputStream out = null;
        InputStream err = null;
        BufferedReader inr = null;
        try {

            Thread monitor = ThreadMonitor.start(timeout);

            proc = openProcess(cmdAttribs);
            in = proc.getInputStream();
            out = proc.getOutputStream();
            err = proc.getErrorStream();
            inr = new BufferedReader(new InputStreamReader(in));
            String line = inr.readLine();
            while (line != null && lines.size() < max) {
                line = line.toLowerCase(Locale.ENGLISH).trim();
                lines.add(line);
                line = inr.readLine();
            }

            proc.waitFor();

            ThreadMonitor.stop(monitor);

            if (proc.exitValue() != 0) {
                // os command problem, throw exception
                throw new IOException("Command line returned OS error code '"
                        + proc.exitValue() + "' for command "
                        + Arrays.asList(cmdAttribs));
            }
            if (lines.size() == 0) {
                // unknown problem, throw exception
                throw new IOException("Command line did not return any info "
                        + "for command " + Arrays.asList(cmdAttribs));
            }
            return lines;

        } catch (InterruptedException ex) {
            throw new IOExceptionWithCause(
                    "Command line threw an InterruptedException "
                            + "for command " + Arrays.asList(cmdAttribs)
                            + " timeout=" + timeout, ex);
        } finally {
            closeQuietly(in);
            closeQuietly(out);
            closeQuietly(err);
            closeQuietly(inr);
            if (proc != null) {
                proc.destroy();
            }
        }
    }

    /**
     * Opens the process to the operating system.
     * 
     * @param cmdAttribs
     *            the command line parameters
     * @return the process
     * @throws IOException
     *             if an error occurs
     */
    static Process openProcess(String[] cmdAttribs) throws IOException {
        return Runtime.getRuntime().exec(cmdAttribs);
    }
}
